题目大意:
就是现在有0~9对应的BCD码(就是对应的4位的二进制), 然后给出了n串(0 <= n <= 1000)不能出现的串(只包含0和1), 现在问在数字A到B之间有多少个数在转换成BCD码表示之后不包含这n个串,1 <= A <= B <= 10^200
大致思路:
首先考虑到A和B的范围,暴力是不可行的,这里需要用到数位DP,也就是逐位确定的思想,首先用AC自动机算出从状态i出发下一位是0~9分别可到达的状态,或者不可走,然后逐位确定各个数位的值即可,记忆化搜索提高效率,启示这个数位DP还是挺简单的,注意前导零的问题即可。
代码如下:
Result : Accepted Memory : 3676 KB Time : 210 ms
/* * Author: Gatevin * Created Time: 2014/11/27 10:18:15 * File Name: Kagome.cpp */ #include<iostream> #include<sstream> #include<fstream> #include<vector> #include<list> #include<deque> #include<queue> #include<stack> #include<map> #include<set> #include<bitset> #include<algorithm> #include<cstdio> #include<cstdlib> #include<cstring> #include<cctype> #include<cmath> #include<ctime> #include<iomanip> using namespace std; const double eps(1e-8); typedef long long lint; const lint mod = 1000000009LL; int n; char num[210]; int bit[210]; int bcd[2010][10]; //bcd[i][j]表示从AC自动机的i状态,下一个数字是j会到达的状态,为-1表示不可走 lint dp[2010][210]; //dp[i][j]表示从AC自动机的i状态,接下来的j位没有边界和前导零限制时的方案数,用于记忆化 struct Trie { int next[2010][2], fail[2010]; bool end[2010]; int L, root; int newnode() { for(int i = 0; i < 2; i++) next[L][i] = -1; end[L++] = 0; return L - 1; } void init() { L = 0; root = newnode(); return; } void insert(char *s) { int now = root; for(; *s; s++) { if(next[now][*s - '0'] == -1) next[now][*s - '0'] = newnode(); now = next[now][*s - '0']; } end[now] = 1; return; } void build() { queue <int> Q; fail[root] = root; Q.push(root); while(!Q.empty()) { int now = Q.front(); Q.pop(); if(end[fail[now]]) end[now] = 1;//标记所有不能到达的点 for(int i = 0; i < 2; i++) if(next[now][i] == -1) next[now][i] = now == root ? root : next[fail[now]][i]; else { fail[next[now][i]] = now == root ? root : next[fail[now]][i]; Q.push(next[now][i]); } } return; } }; Trie AC; void BCD()//计算bcd数组 { for(int i = 0; i < AC.L; i++) for(int j = 0; j < 10; j++) { bcd[i][j] = i; if(AC.end[bcd[i][j]]) { bcd[i][j] = -1; continue; } for(int k = 3; k >= 0; k--) { if(j & (1 << k)) bcd[i][j] = AC.next[bcd[i][j]][1]; else bcd[i][j] = AC.next[bcd[i][j]][0]; if(AC.end[bcd[i][j]]) { bcd[i][j] = -1; break; } } } return; } /* * 返回剩下的长度为L需要填充,当前在AC自动机的pos状态, * bound表示接下来这一位是否考虑上界bit限制 * lead表示之前是否已经有数字,即当前位填0是否有状态转移(前导零不转移) */ lint dfs(int L, int pos, bool bound, bool lead)//写的不怎么优美.... { if(L == 0 && lead) return 1; if(L == 0 && !lead) return 0; if(dp[pos][L] != -1 && !bound && lead) return dp[pos][L]; lint ret = 0; if(!lead)//没有前导零 { ret = (ret + dfs(L - 1, pos, false, false)) % mod;//继续没有前导零 if(bound)//有上界限制 { for(int i = 1; i < bit[L]; i++)//接下来填的数比当前位小,接下去就没有限制 if(bcd[pos][i] != -1) ret = (ret + dfs(L - 1, bcd[pos][i], false, true)) % mod; if(bcd[pos][bit[L]] != -1) ret = (ret + dfs(L - 1, bcd[pos][bit[L]], true, true)) % mod;//有限制 } else for(int i = 1; i < 10; i++)//没有限制就随便填了,接下去也没有限制 if(bcd[pos][i] != -1) ret = (ret + dfs(L - 1, bcd[pos][i], false, true)) % mod; } else { if(bound) { for(int i = 0; i < bit[L]; i++) if(bcd[pos][i] != -1) ret = (ret + dfs(L - 1, bcd[pos][i], false, true)) % mod; if(bcd[pos][bit[L]] != -1) ret = (ret + dfs(L - 1, bcd[pos][bit[L]], true, true)) % mod; } else for(int i = 0; i < 10; i++) if(bcd[pos][i] != -1) ret = (ret + dfs(L - 1, bcd[pos][i], false, true)) % mod; } if(lead && dp[pos][L] == -1 && !bound) dp[pos][L] = ret;//记忆化 return ret; } //计算从0到当前数字有多少是不包含那N个危险串的 lint solve(int len) { if(len == 0) return 0; for(int i = 0; i < len; i++) bit[len - i] = num[i] - '0'; return dfs(len, AC.root, true, false); } int main() { int t; char ts[22]; scanf("%d", &t); while(t--) { scanf("%d", &n); AC.init(); while(n--) { scanf("%s", ts); AC.insert(ts); } AC.build(); BCD(); scanf("%s", num); int len = strlen(num); /* * 将当前的左界减去1 */ if(num[len - 1] > '0') { int tmp = len - 1; if(len == 1 && num[len - 1] == '1') len = 0; num[tmp] = num[tmp] - 1; } else { num[len - 1] = '9'; int now = len - 2; while(num[now] == '0') { num[now] = '9'; now--; } if(num[now] == '1' && now == 0) { for(int i = 0; i < len; i++) num[i] = num[i + 1]; len--; } else num[now] = num[now] - 1; } memset(dp, -1, sizeof(dp)); lint A = solve(len); scanf("%s", num); len = strlen(num); lint B = solve(len); printf("%lld\n", (B - A + mod) % mod); } return 0; }