给定一个长度为 \(l\) 的大数 \(n\),求 \(\leq n\) 且不以子串形式包含给定 \(m\) 字典的数的个数。\(l\leq 1200, m\leq 100, \sum len \leq 1500\)
Solution
设 \(f[i][j][0/1]\) 表示考虑了前 \(i\) 位,走到 \(j\) 结点,下一个字符是否挨着上界的方案数
转移方法有
- \(f[i-1][j][0] \to f[i][ch[j][0..9]][0]\)
- \(f[i-1][j][1] \to f[i][ch[j][0..s[i]-1]][0]\)
- \(f[i-1][j][1] \to f[i][ch[j][s[i]]][1]\)
但是考虑到数字中不能有前导零但是字典中可以有
据说只需要大力删除 \(ch[0][0]\) 就可以解决该问题(大雾)
#include <bits/stdc++.h> using namespace std; const int N = 5005; const int mod = 1e+9 + 7; #define ch c queue <int> q; int n,m,c[N][10],f[N][N][2],val[N],fi[N],cnt,ans[1005]; void ins(char *str,int id) { int len=strlen(str), p=0; for(int i=0; i<len; i++) { int v=str[i]-'0'; if(!c[p][v]) c[p][v]=++cnt; p=c[p][v]; } val[p]=id; } void build() { for(int i=0; i<10; i++) if(c[0][i]) fi[c[0][i]]=0, q.push(c[0][i]); while(!q.empty()) { int u=q.front(); q.pop(); for(int i=0; i<10; i++) if(c[u][i]) fi[c[u][i]]=c[fi[u]][i], q.push(c[u][i]); else c[u][i]=c[fi[u]][i]; } } char str[N],pat[N]; void sh(int &x,int y) { x=(x+y)%mod; } signed main() { ios::sync_with_stdio(false); cin>>str; n=strlen(str); cin>>m; for(int i=1;i<=m;i++) { cin>>pat; ins(pat,i); } build(); ch[0][0]=0; for(int i=1;i<str[0]-'0';i++) sh(f[1][ch[0][i]][0],1); sh(f[1][ch[0][str[0]-'0']][1],1); for(int i=2;i<=n;i++) { for(int j=1;j<10;j++) sh(f[i][ch[0][j]][0],1); for(int j=0;j<=cnt;j++) if(val[j]==0) { for(int k=0;k<=9;k++) { if(val[ch[j][k]]==0) sh(f[i][ch[j][k]][0],f[i-1][j][0]); } for(int k=0;k<str[i-1]-'0';k++) { if(val[ch[j][k]]==0) sh(f[i][ch[j][k]][0],f[i-1][j][1]); } if(val[ch[j][str[i-1]-'0']]==0) sh(f[i][ch[j][str[i-1]-'0']][1],f[i-1][j][1]); } } int ans=0; for(int i=0;i<=cnt;i++) if(val[i]==0) sh(ans,f[n][i][0]+f[n][i][1]); cout<<ans; }
来源:https://www.cnblogs.com/mollnn/p/12453627.html