题目链接:bzoj3530
题解:
这道题很容易可以想到数位dp,如何在保证限制下进行dp呢?
我们可以先将字符串建成ac自动机,这样,我们就可以在dp上加一维表示当前在ac自动机上的哪个点,我们只要dp转移时,不转移给那些不能走的点就可以了,我们用记忆化搜索来解决这个问题。只要在深搜时记录好已经确定的前缀在ac自动机上所走到的节点,我们只从合法状态转移过来就行了。dfs的边界是0,此时返回1。
#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <ctime>
#include <cmath>
#include <algorithm>
using namespace std;
const int L=1210,M=1510;
const int mod=1000000007;
char n[L],s[M];
int son[M][10],end[M],fail[M],line[M];
int f[L][M][2];
bool vis[L][M][2];
int l,cnt,p,root,m;
void insert(){
int now=root,l=strlen(s+1);
for (int i=1;i<=l;i++){
if (!son[now][s[i]-'0'])
son[now][s[i]-'0']=++p;
now=son[now][s[i]-'0'];
}
end[now]=1;
}
void getfail(){
int cl=0,op=1;
line[op]=root;
for (;cl<op;){
int now=line[++cl];
end[now]|=end[fail[now]];
for (int i=0;i<=9;i++)
if (!son[now][i])
son[now][i]=son[fail[now]][i];
else{
if (now!=root)
fail[son[now][i]]=son[fail[now]][i];
line[++op]=son[now][i];
}
}
}
void add(int &a,int b){
a=(a+b)%mod;
}
int dfs(int now,int pos,int zero,int up){
int ans=0;
if (now==l+1)
return 1;
if (zero)
ans=dfs(now+1,pos,zero,0);
if (!up&&vis[now][pos][zero]) return f[now][pos][zero];
int top=9;
if (up) top=n[now]-'0';
for (int i=0;i<=top;i++)
if (!(zero&&i==0)&&end[son[pos][i]]==0)
add(ans,dfs(now+1,son[pos][i],0,up&&i==top));
if (!up){
vis[now][pos][zero]=1;
f[now][pos][zero]=ans;
}
return ans;
}
int main(){
freopen("3530.in","r",stdin);
freopen("3530.out","w",stdout);
scanf("%s",n+1);
l=strlen(n+1);
scanf("%d",&m);
for (int i=1;i<=m;i++){
scanf("%s",s+1);
insert();
}
getfail();
int ans=dfs(1,root,1,1);
ans=(ans-1+mod)%mod;
printf("%d\n",ans);
fclose(stdin);
fclose(stdout);
return 0;
}