【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;
}