[SDOI2014]数数(ac自动机+数位DP)
链接:https://ac.nowcoder.com/acm/problem/20366
来源:牛客网
输入描述:
输入的第一行包含整数N。
接下来一行一个整数M,表示S中元素的数量。
接下来M行,每行一个数字串,表示S中的一个元素。
输出描述:
输出一行一个整数,表示答案模1e9+7的值。
具体思路:首先考虑一下,我们在枚举位数的时候,不合法的情况是当前节点节点的val值为1或者这个节点的fail指针指向的节点在trie树上
的val值为1。然后我们就按照这个思路进行下去,对于每个节点我们求出他的fail指针,然后剩下的额就是数位DP的问题了。
AC代码:
1 #include<bits/stdc++.h> 2 using namespace std; 3 # define ll long long 4 const int mod = 1e9+7; 5 const int maxn = 2e5+100; 6 char str_N[maxn],str[maxn]; 7 int ch[maxn][12],val[maxn],fail[maxn]; 8 int tot; 9 void add_trie(){ 10 int len=strlen(str); 11 int p=0; 12 for(int i=0; i<len; i++){ 13 int to=str[i]-'0'; 14 if(!ch[p][to]) 15 ch[p][to]=++tot; 16 p=ch[p][to]; 17 } 18 val[p]=1; 19 } 20 void get_fail(){ 21 queue<int>q; 22 q.push(0); 23 for(int i=0; i<=tot; i++) 24 fail[i]=-1; 25 while(!q.empty()){ 26 int top=q.front(); 27 q.pop(); 28 for(int i=0; i<10; i++){ 29 int to=ch[top][i]; 30 if(to){ 31 q.push(to); 32 int tmp=fail[top]; 33 while(tmp!=-1&&!ch[tmp][i]) 34 tmp=fail[tmp]; 35 fail[to]=(tmp==-1 ? 0 : ch[tmp][i]); 36 val[to]|=val[ch[fail[top]][i]];// 这里需要或上他的fail指针,只要有1就可以了。 37 } 38 else 39 { 40 ch[top][i]=ch[fail[top]][i]; // 注意这里的,每一颗数的节点都应为10个,如果在原来的trie树上没有这个节点,那么就继承这条链上合法的最后一个节点 41 } 42 } 43 } 44 } 45 int a[maxn]; 46 ll dp[2000+10][2000+10]; 47 ll dfs(int pos,int is_head,int is_zero,int p)// 注意前导0, 23的子串中不包含 023 48 { 49 if(!pos) 50 return val[pos]==0; 51 if(!is_head&&!is_zero&&dp[pos][p]!=-1) 52 return dp[pos][p]; 53 ll ans=0; 54 int fmax = is_head ? a[pos] : 9; 55 for(int i=0; i<=fmax; i++){ 56 if(is_zero&&i==0) 57 ans=(ans+dfs(pos-1,is_head&&i==fmax,1,0)%mod)%mod;// 从0开始 58 else{ 59 if(val[ch[p][i]])// 当当前节点非法的时候,停止搜索 60 continue; 61 ans=(ans+dfs(pos-1,is_head&&i==fmax,is_zero&&i==0,ch[p][i])%mod)%mod; 62 } 63 } 64 if(!is_head&&!is_zero) 65 dp[pos][p]=ans; 66 return ans; 67 } 68 ll solve() 69 { 70 memset(dp,-1,sizeof(dp)); 71 int len=strlen(str_N); 72 reverse(str_N,str_N+len); 73 for(int i=0; i<len; i++){ 74 a[i+1]=str_N[i]-'0'; 75 } 76 return dfs(len,1,1,0); 77 } 78 int main(){ 79 scanf("%s",str_N);// 输入的是按照自字符串读入 80 int m; 81 scanf("%d",&m); 82 while(m--){ 83 scanf("%s",str); 84 add_trie(); 85 } 86 get_fail(); 87 ll ans=solve()-1; 88 printf("%lld\n",ans); 89 return 0; 90 }
SRE实战 互联网时代守护先锋,助力企业售后服务体系运筹帷幄!一键直达领取阿里云限量特价优惠。

更多精彩