【BZOJ3530】[Sdoi2014]数数
Description
我们称一个正整数N是幸运数,当且仅当它的十进制表示中不包含数字串集合S中任意一个元素作为其子串。例如当S=(22,333,0233)时,233是幸运数,2333、20233、3223不是幸运数。
给定N和S,计算不大于N的幸运数个数。
Input
输入的第一行包含整数N。
接下来一行一个整数M,表示S中元素的数量。
接下来M行,每行一个数字串,表示S中的一个元素。
Output
输出一行一个整数,表示答案模109+7的值。
Sample Input
20
3
2
3
14
3
2
3
14
Sample Output
14
HINT
下表中l表示N的长度,L表示S中所有串长度之和。
1 < =l < =1200 , 1 < =M < =100 ,1 < =L < =1500
题解:本人闲着蛋疼出了套题,考完后同学说这题PPT里有,于是看PPT发现真的有。。。于是跑来水一发~
先将所有S中的数拿出来建一个AC自动机,然后用f[i][j]表示从AC自动机上的节点i开始走j步有多少种走法。然后数位DP即可。
注意前导0的情况。
#include <cstdio> #include <cstring> #include <iostream> #include <queue> using namespace std; const int mod=1000000007; int n,m,tot; struct node { int ch[10],fail,cnt; }p[1510]; int v[1210]; char str[1210]; int ans,f[1210][1510]; queue<int> q; void build() { int i,j,k,u,v; q.push(1); while(!q.empty()) { u=q.front(),q.pop(); for(i=0;i<=9;i++) { if(!p[u].ch[i]) { if(u==1) p[u].ch[i]=1; else p[u].ch[i]=p[p[u].fail].ch[i]; continue; } v=p[u].ch[i],q.push(v); if(u==1) p[v].fail=1; else p[v].fail=p[p[u].fail].ch[i],p[v].cnt|=p[p[v].fail].cnt; } } for(i=1;i<=tot;i++) if(!p[i].cnt) f[0][i]=1; for(j=1;j<=n;j++) for(i=1;i<=tot;i++) for(k=0;k<=9;k++) if(!p[i].cnt) f[j][i]=(f[j][i]+f[j-1][p[i].ch[k]])%mod; } int main() { int i,j,a,b,u; scanf("%s",str),n=strlen(str); for(i=1;i<=n;i++) v[i]=str[n-i]-'0'; scanf("%d",&m); tot=1; for(i=1;i<=m;i++) { scanf("%s",str),a=strlen(str); for(u=1,j=0;j<a;j++) { b=str[j]-'0'; if(!p[u].ch[b]) p[u].ch[b]=++tot; u=p[u].ch[b]; } p[u].cnt=1; } build(); for(i=1;i<n;i++) for(j=1;j<=9;j++) ans=(ans+f[i-1][p[1].ch[j]])%mod; for(u=1,i=n;i>=1;i--) { for(j=(i==n)?1:0;j<v[i];j++) ans=(ans+f[i-1][p[u].ch[j]])%mod; u=p[u].ch[v[i]]; if(p[u].cnt) break; } if(!p[u].cnt) ans=(ans+1)%mod; printf("%d",ans); return 0; }