链接
http://www.lydsy.com/JudgeOnline/problem.php?id=3530
题解
一个小错查了一下午,建trie图的时候我把0当做串的终止,可题目中明明要求0是合法的。。。
唉。。
f[i][j]表示长度为i的数字匹配到j点的方案数(允许前导零没有大小限制),g[i][j]是有大小限制的。
然后直接dp就行了(ac自动机上的dp道路+数位dp套路)。
答案的统计:这个有点费脑子,枚举位数,枚举下一位从1~9,然后计入ans就行了。(其实不费脑子但是我今天有点智障)
代码
//AC自动机上的数位dp
#include <cstdio>
#include <algorithm>
#include <queue>
#include <cstring>
#define maxn 2000
#define mod 1000000007
using namespace std;
int num[maxn], trie[maxn][10], tail[maxn], tot=1, fail[maxn], N, f[maxn][maxn],
g[maxn][maxn], h[maxn][maxn];
queue<int> q;
void insert(int *s)
{
int p;
for(p=1;*s!=-1 and !tail[p];s++)p=trie[p][*s]?trie[p][*s]:trie[p][*s]=++tot;
tail[p]=1;
}
int acamove(int pos, int x)
{
for(;pos and !trie[pos][x];pos=fail[pos]);
return pos?trie[pos][x]:1;
}
void acabuild()
{
int u, v, i;
q.push(1);
while(!q.empty())
{
u=q.front();q.pop();
for(i=0;i<=9;i++)
{
if(trie[u][i])
{
v=acamove(fail[u],i);
fail[trie[u][i]]=v;
q.push(trie[u][i]);
}
else trie[u][i]=acamove(u,i);
}
}
}
void input()
{
char s[maxn];
int t[maxn];
int M, i, j, l;
scanf("%s%d",s,&M);
N=strlen(s);
for(i=0;i<N;i++)num[N-i]=s[i]-48;
for(i=1;i<=M;i++)
{
scanf("%s",s);
l=strlen(s);
for(j=0;s[j];j++)t[j]=s[l-j-1]-48;t[l]=-1;
insert(t);
}
}
int dp()
{
int i, j, now, ans=0;
f[0][1]=g[0][1]=1;
for(i=0;i<N;i++)
for(j=1;j<=tot;j++)
{
if(tail[j] or (!f[i][j] and !g[i][j]))continue;
for(now=0;now<=9;now++)
f[i+1][trie[j][now]]=(f[i+1][trie[j][now]]+f[i][j])%mod;
for(now=0;now<=num[i+1];now++)
{
if(now!=num[i+1])
g[i+1][trie[j][now]]=(g[i+1][trie[j][now]]+f[i][j])%mod;
else g[i+1][trie[j][now]]=(g[i+1][trie[j][now]]+g[i][j])%mod;
}
if(i<N-1)
{
for(now=1;now<=9;now++)
if(!tail[trie[j][now]])ans=(ans+f[i][j])%mod;
}
else
{
for(now=1;now<=num[N];now++)
{
if(tail[trie[j][now]])continue;
if(now==num[N])ans=(ans+g[i][j])%mod;
else ans=(ans+f[i][j])%mod;
}
}
}
return ans;
}
int main()
{
input();
acabuild();
printf("%d",dp());
return 0;
}