显然直接 $AC$ 自动机上数位 $dp$ 一下
预处理出 $f[i][j]$ 表示当前匹配到 $AC$ 自动机上的节点 $j$ ,再放 $i$ 个位的数字后不冲突的方案数
初始时 $f[0][j]=1$ ,其中 $j$ 不是匹配节点(匹配节点显然指的是本身是某个模式串的结束节点或者 $fail$ 树上祖先存在某个节点是结束节点)
然后把 $n$ 放到自动机上走,其实就是普通的数位 $dp$ ,每到一个节点都枚举所有小于这一位的数字,然后把 $f$ 相应的方案数加入答案
然后一旦 $n$ 走到自动机的匹配节点了就直接 $return$
如果 $n$ 走完了也没有到匹配节点,那么等于 $n$ 的这个数也是一种合法方案,要记得答案 $+1$,那么数位 $dp$ 的代码很简单:
void dfs(int x,int p)//当前在节点x,考虑到n的第p位 { if(pd[x]) return;//不合法了直接return if(p>tot) { ans++; return; }//n是合法的答案要包括n for(int i=0;i<n[p]-'0';i++) ans=(ans+f[tot-p][c[x][i]])%mod;//枚举这一位小于n[p]的数,剩下的位就可以随便填 dfs(c[x][n[p]-'0'],p+1); }
然后发现只有 $80$ 分,这是因为模式串可能有前导 $0$ ,我们预处理 $f$ 的时候也是有考虑前导 $0$ 的情况,但是真正填数的时候是没有前导零的
所以在 $n$ 的第一位之前,我们枚举小于 $n[1]$ 的数时不能枚举 $0$ ,因为这样就变成了有前导零的情况
我们必须在最后再特殊考虑位数不足 $n$ 的情况:
for(int i=0;i<tot-1;i++)//枚举总位数不足n的位数的情况 for(int j=1;j<=9;j++) ans=(ans+f[i][c[0][j]])%mod;//枚举第一个数
#include<iostream> #include<cstdio> #include<algorithm> #include<cstring> #include<cmath> #include<queue> using namespace std; typedef long long ll; inline int read() { int x=0,f=1; char ch=getchar(); while(ch<'0'||ch>'9') { if(ch=='-') f=-1; ch=getchar(); } while(ch>='0'&&ch<='9') { x=(x<<1)+(x<<3)+(ch^48); ch=getchar(); } return x*f; } const int N=2007,mo=1e9+7; inline int fk(int x) { return x>=mo ? x-mo : x; } int tot,m; char s[N],n[N]; int c[N][11],fail[N],cnt; bool pd[N]; void ins() { int u=0,len=strlen(s+1); for(int i=1;i<=len;i++) { int v=s[i]-'0'; if(!c[u][v]) c[u][v]=++cnt; u=c[u][v]; } pd[u]=1; } void build() { queue <int> Q; for(int i=0;i<=9;i++) if(c[0][i]) Q.push(c[0][i]); while(!Q.empty()) { int x=Q.front(); Q.pop(); for(int i=0;i<=9;i++) { int &v=c[x][i]; if(!v) v=c[fail[x]][i]; else fail[v]=c[fail[x]][i],pd[v]|=pd[fail[v]],Q.push(v); } } } int f[N][N],ans; void pre() { for(int i=0;i<=cnt;i++) if(!pd[i]) f[0][i]=1; for(int i=1;i<=tot;i++) for(int j=0;j<=cnt;j++) { if(pd[j]) continue; for(int k=0;k<=9;k++) f[i][j]=fk(f[i][j]+f[i-1][c[j][k]]); } } void dfs(int x,int p) { if(pd[x]) return; if(p>tot) { ans++; return; } for(int i=(p==1);i<n[p]-'0';i++) ans=fk(ans+f[tot-p][c[x][i]]); dfs(c[x][n[p]-'0'],p+1); } int main() { scanf("%s",n+1); m=read(); tot=strlen(n+1); for(int i=1;i<=m;i++) scanf("%s",s+1),ins(); build(); pre(); dfs(0,1); for(int i=0;i<tot-1;i++) for(int j=1;j<=9;j++) ans=fk(ans+f[i][c[0][j]]); printf("%d ",ans); return 0; }