P3311 [SDOI2014]数数 AC自动机+数位DP
题意
给定一个正整数N和n个模式串,问不大于N的数字中有多少个不包含任意模式串,输出对\(1e^9+7\)取模后的答案。
解题思路
把所有模式串都加入AC自动机,然后跑数位DP就好了。需要注意的是,这题需要考虑前导0的影响。举个栗子,对于N=2333,在处理数字233时其实处理的是"0233",这时如果不考虑前导0且模式串又包含"0233"的话就会出错。
消除前导0的影响也很简单,特判一下就可以了,具体请见代码。
AC代码
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn=2e5+5;
const int mod=1e9+7;
int len,n;
char s[2019],t[2019];
struct AC_Automaton{
//root=0,range[1,tot]
const static int SIZE=10;
int tr[maxn][SIZE],fail[maxn],tot;
int val[maxn];
ll dp[2019][2019];
inline int newnode(){
int p=++tot;
memset(tr[p],0,sizeof(tr[p]));
val[p]=0;
return p;
}
inline void init(){
tot=0;
memset(tr[0],0,sizeof(tr[0]));
val[0]=0;
}
inline void insert(char *s){
int p=0;
for(int i=0;s[i];++i){
if(!tr[p][s[i]-'0'])tr[p][s[i]-'0']=newnode();
p=tr[p][s[i]-'0'];
}
val[p]=1;
}
inline void getfail(){
queue<int>q;
for(int i=0;i<SIZE;i++)if(tr[0][i])fail[tr[0][i]]=0,q.push(tr[0][i]);
while(!q.empty()){
int p=q.front();q.pop();
for(int i=0;i<SIZE;i++){
if(tr[p][i]){
fail[tr[p][i]]=tr[fail[p]][i],q.push(tr[p][i]);
}
else tr[p][i]=tr[fail[p]][i];
}
}
}
ll dfs(int p,int pos,int jud,int zero){
if(pos==len)return !zero;
if(!jud && dp[p][pos]!=-1)return dp[p][pos];
int sz=jud?t[pos]-'0':9;
ll res=0;
for(int i=0;i<=sz;i++){
int tmp=tr[p][i];
if(val[tmp])continue;
res+=dfs(zero?tr[0][i]:tmp,pos+1,jud&&(i==sz),zero&&(i==0));
//若是前导0则从根节点开始转移
res%=mod;
}
if(!jud && !zero)dp[p][pos]=res;
return res;
}
inline void solve(char *t){
len=strlen(t);
memset(dp,-1,sizeof(dp));
printf("%lld\n",dfs(0,0,1,1));
}
}A;
int main()
{
//#ifndef ONLINE_JUDGE
// freopen("in.txt","r",stdin);
//#endif
A.init();
scanf("%s",t);
scanf("%d",&n);
for(int i=1;i<=n;i++){
scanf("%s",s);
A.insert(s);
}
A.getfail();
A.solve(t);
return 0;
}