3530: [Sdoi2014]数数
Time Limit: 10 Sec Memory Limit: 512 MBSubmit: 814 Solved: 423
[ Submit][ Status][ Discuss]
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
Source
题解:AC自动机+数位DP
对于S中的元素,建立AC自动机,打isend标记的时候注意如果该点fail指针指向的节点有isend标记,那么这个点也应该有isend标记。
然后进行数位dp,f[i][j][0/1][0/1]表示是第i位匹配到AC自动机的第j个节点是否卡上下界的方案数。
转移的时候注意不要转移到有isend标记的节点上。
因为这道题我们统计的数是不包括前导零的,所以需要特判,如果之前一直是0,那么我们就让匹配的节点一直停在根节点。
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cmath>
#include<cstring>
#include<queue>
#define N 2300
#define p 1000000007
#define LL long long
using namespace std;
int ch[N*10][13],isend[N*10],a[N],n,m,cnt,b[N],fail[N*10];
char s[N],s1[N];
LL f[N][N][2][2];
void insert(char s[])
{
int len=strlen(s+1); int now=0;
for (int i=1;i<=len;i++) {
int x=s[i]-'0';
if (ch[now][x]) now=ch[now][x];
else ch[now][x]=++cnt,now=cnt;
}
isend[now]=1;
}
void makefail()
{
int now=0;
queue<int> q;
for (int i=0;i<=9;i++)
if (ch[now][i]) q.push(ch[now][i]),fail[ch[now][i]]=0;
while (!q.empty()) {
int now=q.front(); q.pop();
for (int i=0;i<=9;i++){
if (!ch[now][i]) {
ch[now][i]=ch[fail[now]][i];
continue;
}
int t=fail[now];
fail[ch[now][i]]=ch[t][i];
if (isend[ch[t][i]]) isend[ch[now][i]]=1;
q.push(ch[now][i]);
}
}
}
int main()
{
freopen("A.in","r",stdin);
freopen("A.out","w",stdout);
scanf("%s",s+1);
n=strlen(s+1);
for (int i=1;i<=n;i++) a[i]=s[i]-'0';
scanf("%d",&m);
for (int i=1;i<=m;i++) {
scanf("%s",s1+1);
insert(s1);
}
int now=0;
for (int i=0;i<=9;i++)
if (!ch[now][i]) ch[now][i]=++cnt;
makefail();
for (int i=1;i<=n;i++) b[i]=0; b[n]=1;
//for (int i=1;i<=n;i++) cout<<a[i];
//cout<<endl;
//for (int i=1;i<=n;i++) cout<<b[i];
//cout<<endl;
memset(f,0,sizeof(f));
if (n!=1) f[1][now][1][0]++;
for (int i=1;i<=9;i++)
if (!isend[ch[now][i]])
{
if (i<b[1]||i>a[1]) continue;
if (i>b[1]&&i<a[1]) f[1][ch[now][i]][0][0]++;
else if (i==b[1]&&i==a[1]) f[1][ch[now][i]][1][1]++;
else if (i==b[1]) f[1][ch[now][i]][1][0]++;
else if (i==a[1]) f[1][ch[now][i]][0][1]++;
}
for (int i=1;i<n;i++)
for (int j=0;j<=cnt;j++)
for (int a1=0;a1<=1;a1++)
for (int b1=0;b1<=1;b1++)
if (f[i][j][a1][b1]) {
//cout<<i<<" "<<j<<" "<<a1<<" "<<b1<<" "<<f[i][j][a1][b1]<<endl;
for (int x=0;x<=9;x++)
{
if (b1&&x>a[i+1]||a1&&x<b[i+1]) continue;
if (ch[j][x]) {
int t=ch[j][x];
if (a1&&x==0) t=now;
if (!isend[t]) {
int pd=0; int pd1=0;
if (b1&&a[i+1]==x) pd=1;
if (a1&&b[i+1]==x) pd1=1;
f[i+1][t][pd1][pd]=(f[i+1][t][pd1][pd]+f[i][j][a1][b1])%p;
}
}
else {
int t=ch[0][x];
if (a1&&x==0) t=now;
if (!isend[t]) {
int pd=0; int pd1=0;
if (b1&&a[i+1]==x) pd=1;
if (a1&&b[i+1]==x) pd1=1;
f[i+1][t][pd1][pd]=(f[i+1][t][pd1][pd]+f[i][j][a1][b1])%p;
}
}
}
}
LL ans=0;
for (int i=0;i<=cnt;i++)
for (int a1=0;a1<=1;a1++)
for (int b1=0;b1<=1;b1++)
ans=(ans+f[n][i][a1][b1])%p;
printf("%lld\n",ans);
}