bzoj 3530 : [Sdoi2014]数数
一眼AC自动机上DP,f[i][j][0]到第j个节点表示前i位与n一样的方案,1表示不一样的方案。
事实证明我每回写数位dp都要wa到神志模糊,数据一有0就跪,所以加了各种特判。。。
因为如果数据中有0032这种的话直接dp会把32判为不合法,而事实上它是合法的,因为要去掉前缀0.
所以每回从根向0的边转移时要少转移1,相当于把0000....这个全为0的前缀保留在根,为了保留这个前缀还要从第二次开始每次f[i][0][1]++。
好像每次写数位dp都是加了一堆特判才过的。。。
1 #include<iostream> 2 #include<cstdio> 3 #include<cstring> 4 #include<queue> 5 #include<algorithm> 6 #define p 1000000007 7 using namespace std; 8 int m,n; 9 char s[2000]; 10 int ch[2000][10];int cnt; 11 bool v[2000]; 12 bool flag; 13 void build() 14 { 15 int len=strlen(s); 16 int now=0; 17 for(int i=0;i<len;i++) 18 { 19 int c=s[i]-'0'; 20 if(ch[now][c])now=ch[now][c]; 21 else 22 { 23 ch[now][c]=++cnt; 24 now=ch[now][c]; 25 } 26 } 27 v[now]=1; 28 } 29 queue<int>q; 30 int f[2000]; 31 int ans; 32 void AC() 33 { 34 for(int i=0;i<10;i++) 35 { 36 if(ch[0][i])q.push(ch[0][i]); 37 } 38 while(!q.empty()) 39 { 40 int tmp=q.front();q.pop(); 41 for(int i=0;i<10;i++) 42 { 43 if(ch[tmp][i]) 44 { 45 f[ch[tmp][i]]=ch[f[tmp]][i]; 46 q.push(ch[tmp][i]); 47 if(v[f[ch[tmp][i]]])v[ch[tmp][i]]=1; 48 } 49 else 50 { 51 ch[tmp][i]=ch[f[tmp]][i]; 52 } 53 } 54 } 55 } 56 int now[2000][2],pre[2000][2]; 57 char a[2000]; 58 int be[2000]; 59 int main() 60 { 61 scanf("%s",a); 62 n=strlen(a); 63 scanf("%d",&m); 64 for(int i=1;i<=m;i++) 65 { 66 scanf("%s",s); 67 build(); 68 } 69 AC(); 70 pre[0][0]=1; 71 for(int i=0;i<n;i++) 72 { 73 int x=a[i]-'0'; 74 if(i)pre[0][1]++; 75 for(int j=0;j<=cnt;j++) 76 { 77 if(v[j])continue; 78 for(int k=0;k<x;k++) 79 { 80 if(!i&&!k)continue; 81 now[ch[j][k]][1]+=pre[j][0]; 82 now[ch[j][k]][1]%=p; 83 } 84 now[ch[j][x]][0]+=pre[j][0]; 85 now[ch[j][x]][0]%=p; 86 for(int k=0;k<10;k++) 87 { 88 now[ch[j][k]][1]+=pre[j][1]-(!j&&k==0&&i!=0); 89 now[ch[j][k]][1]%=p; 90 } 91 } 92 for(int j=0;j<=cnt;j++) 93 { 94 if(v[j])continue; 95 if(i==n-1) 96 { 97 ans+=now[j][1]; 98 if(ans>=p)ans-=p; 99 ans+=now[j][0]; 100 if(ans>=p)ans-=p; 101 } 102 pre[j][0]=now[j][0]; 103 pre[j][1]=now[j][1]; 104 now[j][0]=now[j][1]=0; 105 } 106 } 107 printf("%d\n",ans); 108 return 0; 109 }