一道很强大的数位dp+ac自动机题。题目要求a~b间有多少个,可以先求0~b有多少个减去0~a-1有多少个。
我们可以想到dp[i][j][k] 表示长度为i第一个数字为j匹配到k这个结点点个数,转移时枚举下个数字,然后吧这个数字分成4位二进制数,把k转移到f注意转移到时候要判断是否包含非法串。j可以等于0.
注意求值的时候,对于第一个数字是不能等于0的,而后面的考验等于0,其他的就是普通数位dp的解法。
注意点:1 、左区间减1是可能会出现前置0.
2、注意建trie树的顺序,因为数位dp是从前往后,所以建树的时候要把字符串反过来建,我就被这个地方坑了好久。
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#include<queue>
#include<vector>
#define mod 1000000009
using namespace std;
int ch[2005][2];
int fail[2005];
int val[2005];
int vis[2005];
char c[105][25];
char a[205],b[205];
int to[2005][12],tot;
int dp[205][10][2005];
int ss[205][10];
int x[5];
void init(void){
memset(ch,0,sizeof(ch));
memset(fail,0,sizeof(fail));
memset(val,0,sizeof(val));
memset(vis,0,sizeof(vis));
memset(to,0,sizeof(to));
}
void get_trie(int n){
int i,j,f,p;
tot=0;
for(i=0;i<n;i++){
p=(int)strlen(c[i]);
f=0;
for(j=p-1;j>=0;j--){
if(ch[f][c[i][j]-'0']) f=ch[f][c[i][j]-'0'];
else{
ch[f][c[i][j]-'0']=++tot;
f=tot;
}
}
val[f]=1;
}
}
queue<int >q;
int pan(int p){
int i,j,f;
j=p;
for(i=0;i<4;i++){
while(j&&!ch[j][x[i]]) j=fail[j];
j=ch[j][x[i]];
f=j;
while(f){
if(val[f]) return -1;
f=fail[f];
}
}
return j;
}
void get_fail(void){
int i,j,fa,p;
for(i=0;i<2;i++) if(ch[0][i]) q.push(ch[0][i]);
while(!q.empty()){
p=q.front();
q.pop();
for(j=0;j<2;j++){
if(ch[p][j]){
q.push(ch[p][j]);
fa=fail[p];
while(fa&&!ch[fa][j]) fa=fail[fa];
fa=ch[fa][j];
fail[ch[p][j]]=fa;
}
}
}
for(i=0;i<=tot;i++){
p=i;
while(p){
if(val[p]){
vis[i]=1;
break;
}
p=fail[p];
}
}
for(i=0;i<=tot;i++){
for(j=0;j<=9;j++){
for(p=0;p<4;p++){
if(j&(1<<p)) x[p]=1;
else x[p]=0;
}
to[i][j]=pan(i);
}
}
}
void solve(void){
memset(dp,0,sizeof(dp));
dp[0][0][0]=1;
int i,j,p,f;
for(i=0;i<200;i++){
for(j=0;j<10;j++){
for(p=0;p<=tot;p++){
if(!dp[i][j][p]) continue;
for(f=0;f<10;f++){
if(to[p][f]!=-1){
dp[i+1][f][to[p][f]]+=dp[i][j][p];
dp[i+1][f][to[p][f]]%=mod;
}
}
}
}
}
memset(ss,0,sizeof(ss));
for(i=1;i<=200;i++){
for(j=0;j<10;j++){
for(p=0;p<=tot;p++){
ss[i][j]+=dp[i][j][p];
ss[i][j]%=mod;
}
}
}
}
int st[205],tear;
int panduan(int p){
if(vis[p]) return 0;
int i,j;
j=p;
for(i=tear-1;i>=0;i--){
j=to[j][st[i]];
if(j==-1||vis[j]) return 0;
if(tear-1-i+1>=10)break;
}
return 1;
}
int get(char *T){
int i,j,s,n;
s=0;
n=(int)strlen(T);
tear=0;
for(i=1;i<T[0]-'0';i++){
s+=ss[n][i];
s%=mod;
}
for(i=n-2;i>=0;i--){
for(j=1;j<=9;j++){
s+=ss[i+1][j];
s%=mod;
}
}
s+=ss[1][0];
s%=mod;
if(n==1&&T[0]=='0') return s;
st[tear++]=T[0]-'0';
for(i=1;i<n;i++){
for(j=0;j<T[i]-'0';j++){
for(int f=0;f<=tot;f++){
if(panduan(f)){
s+=dp[n-i][j][f];
s%=mod;
}
}
}
st[tear++]=T[i]-'0';
if(!panduan(0)) break;
}
if(panduan(0)) s++;
s%=mod;
return s;
}
int main()
{
int i,n,m,p,t;
cin>>t;
while(t--){
scanf("%d",&n);
init();
for(i=0;i<n;i++){
scanf("%s",c[i]);
}
get_trie(n);
get_fail();
solve();
scanf("%s%s",a,b);
n=(int)strlen(a);
a[n-1]-=1;
p=n-1;
while(a[p]<'0'){
a[p]+=10;
a[p-1]--;
p--;
}
if(a[0]=='0'){
if(n!=1){
for(i=0;i<n-1;i++) a[i]=a[i+1];
a[n-1]='\0';
}
}
m=get(b)-get(a);
m=(m%mod+mod)%mod;
printf("%d\n",m);
}
return 0;
}