AC自动机 + 数位DP
题目大意:
就是现在有0~9对应的BCD码(就是对应的4位的二进制), 然后给出了n串(0 <= n <= 1000)不能出现的串(只包含0和1), 现在问在数字A到B之间有多少个数在转换成BCD码表示之后不包含这n个串,1 <= A <= B <= 10^200
大致思路:
首先将所有的不能出现的串建立一个ac自动机,然后用end数组表示某个节点是否可达。利用ac自动机和end数组就可以求出转移矩阵matrix,matrix[i][j]表示当前在ac自动机的i节点,碰到j时应转移到哪个状态。然后根据这个数组进行数位DP,dp[i][i]表示当前在第i位,在ac自动机中为j的答案数。具体看代码
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <cmath>
#include <cstdlib>
#include <cctype>
#include <string>
#include <iostream>
#include <vector>
#include <map>
#include <set>
#include <stack>
#include <queue>
#include <ctime>
using namespace std;
typedef long long ll;
const int N = 3e5+5;
const int maxnode = 2005;
const int sigma_size = 2;
const int MOD = 1000000009;
char str[205];
struct Trie {
int next[maxnode][sigma_size], fail[maxnode], end[maxnode];
int root, L;
int matrix[maxnode][10];
int DIG[205];
ll dp[205][maxnode]; //dp[i][j]表示当前为第i位,在ac自动机中为第j个节点的答案数
int newNode() {
for(int i = 0; i < sigma_size; i++)
next[L][i] = -1;
end[L] = 0;
return L++;
}
void init() {
L = 0;
root = newNode();
}
void insert(char *buf) {
int len = strlen(buf);
int now = root;
for(int i = 0; i < len; i++) {
int id = buf[i] - '0';
if(next[now][id] == -1)
next[now][id] = newNode();
now = next[now][id];
}
end[now] = 1;
}
void build() {
queue<int> q;
fail[root] = root;
for(int i = 0; i < sigma_size; i++) {
if(next[root][i] == -1) {
next[root][i] = root;
}
else {
fail[next[root][i]] = root;
q.push(next[root][i]);
}
}
while(!q.empty()) {
int now = q.front();
q.pop();
end[now] |= end[fail[now]]; //若该节点的fail节点是不正确的节点,则该节点也是
for(int i = 0; i < sigma_size; i++) {
if(next[now][i] == -1) {
next[now][i] = next[fail[now]][i];
} else {
fail[next[now][i]] = next[fail[now]][i];
q.push(next[now][i]);
}
}
}
}
void getMatrix() { //得到转移矩阵
for(int i = 0; i < L; i++) {
for(int j = 0; j < 10; j++) {
matrix[i][j] = i;
if(end[i])
matrix[i][j] = -1;
int now = i;
for(int k = 3; k >= 0; k--) { //每个数字用四位二进制表示
int x = (j >> k) & 1;
if(end[next[now][x]]) {
matrix[i][j] = -1;
break;
}
else matrix[i][j] = next[now][x];
now = next[now][x];
}
}
}
}
ll dfs(int pos, int cur, int limit, int lead) {
if(pos == 0) return 1;
if(dp[pos][cur] != -1 && !limit && !lead)
return dp[pos][cur];
int end = limit ? DIG[pos] : 9;
ll ret = 0;
for(int i = 0; i <= end; i++) {
if(lead && i == 0) //若有前导零且当前数字为0,就不转移,还在当前节点
ret += dfs(pos-1, cur, limit&&i==end, lead);
else if(matrix[cur][i] != -1) //当可以转移时就转移
ret += dfs(pos-1, matrix[cur][i],limit&&i==end,0);
ret %= MOD;
}
if(!limit && !lead) dp[pos][cur] = ret;
return ret;
}
ll solve(char *buf) { //计算
int len = strlen(buf);
for(int i = 0; i < len; i++) {
DIG[len - i] = buf[i] - '0';
}
return dfs(len, root, 1, 1);
}
}ac;
void Dec(char *buf) { //减一
int len = strlen(buf);
for(int i = len-1; i >= 0; i--) {
if(buf[i] > '0') {
buf[i] --;
return ;
}
else buf[i] = '9';
}
}
int main() {
int t;
cin >> t;
int n;
while(t--) {
memset(ac.dp,-1,sizeof(ac.dp));
ac.init();
scanf("%d",&n);
for(int i = 0; i < n; i++) {
scanf("%s",str);
ac.insert(str);
}
ac.build();
ac.getMatrix();
scanf("%s",str);
Dec(str);
ll ans1 = ac.solve(str);
scanf("%s",str);
ll ans2 = ac.solve(str);
ans2 = (ans2 - ans1 + MOD) % MOD;
printf("%lld\n",ans2);
}
return 0;
}