Description
Data Constraint
Solution
考场上没想到挺可惜的,明明以前做过两次的~
我们将正整数放到一个AC自动机上跑。做一个数位dp,设f[i][j][k][0..1]表示当前到第i位,在自动机上节点j包含k个秘钥,前i位是否与上界相同的方案。我们每次枚举下一位放的数字,看一下AC自动机会跳至哪里转移一下。复杂度O(S*s*k*10)。
Code
#include<iostream>
#include<cmath>
#include<cstring>
#include<cstdio>
#include<algorithm>
#define ll long long
using namespace std;
const ll maxn=505,mo=1e9+7;
ll f[maxn][10],g[maxn][205][11][2],d[maxn],fa[maxn],v[maxn],a[maxn],mi[maxn],b[maxn];
ll n,m,i,t,j,k,l,x,y,z,num,ans;
char s[maxn],s1[maxn],s2[maxn];
void bfs(){
int i=0,j=1,x,k;
while (i<j){
x=v[++i];
for (k=0;k<10;k++){
if (!f[x][k]) continue;
y=fa[x];
while (!f[y][k] && y) y=fa[y];
v[++j]=f[x][k];if (f[y][k]!=f[x][k])fa[v[j]]=f[y][k];
d[v[j]]+=d[fa[v[j]]];
}
}
}
ll dg(){
memset(g,0,sizeof(g));
ll i,j,k,l,x,y=0;b[a[0]+1]=1;
for (i=a[0];i>=1;i--)
b[i]=(b[i+1]+mi[a[0]-i]*a[i]%mo)%mo;
g[0][0][0][1]=1;
for (i=0;i<=a[0];i++)
for (j=0;j<=num;j++){
for (k=0;k<m;k++){
if (i==a[0]) break;
if (g[i][j][k][0]){
for (l=0;l<=9;l++){x=j;
while (!f[x][l] && x) x=fa[x];
if (f[x][l]) x=f[x][l];
g[i+1][x][min(m,k+d[x])][0]=(g[i][j][k][0]+g[i+1][x][min(m,k+d[x])][0])%mo;
}
}
if (g[i][j][k][1]){
for (l=0;l<a[i+1];l++){x=j;
while (!f[x][l] && x) x=fa[x];
if (f[x][l]) x=f[x][l];
g[i+1][x][min(m,k+d[x])][0]=(g[i][j][k][1]+g[i+1][x][min(m,k+d[x])][0])%mo;
}
x=j;
while (!f[x][l] && x) x=fa[x];
if (f[x][l]) x=f[x][l];
g[i+1][x][min(m,k+d[x])][1]=(g[i][j][k][1]+g[i+1][x][min(m,k+d[x])][1])%mo;
}
}
y=(y+g[i][j][m][0]*mi[a[0]-i]%mo)%mo;
y=(y+g[i][j][m][1]*b[i+1]%mo)%mo;
}
return y;
}
int main(){
freopen("word.in","r",stdin);freopen("word.out","w",stdout);
scanf("%lld%lld",&n,&m);
scanf("%s%s\n",s+1,s1+1);t=strlen(s+1);mi[0]=1;
for (i=1;i<=500;i++)mi[i]=mi[i-1]*10%mo;
for (i=1;i<=t;i++)a[i]=s[i]-48;a[0]=t;
for (i=1;i<=n;i++){
x=0;
scanf("%s\n",s2+1);t=strlen(s2+1);
for (j=1;j<=t;j++){
if (!f[x][s2[j]-48]) f[x][s2[j]-48]=++num;
x=f[x][s2[j]-48];
}
d[x]++;
}
bfs();
ans=-dg();t=strlen(s1+1);
for (i=1;i<=t;i++)a[i]=s1[i]-48;a[0]=t;
ans=(ans+dg()+mo)%mo;
printf("%lld\n",ans);
}