真的很开心呢,总算是有一道完完全全由自己做出来的题目啦~
这一道题目洛谷P3311和另一道JSOI文本生成器的题目是十分相像的,dp方面几乎相同。只是<=n的约束,让这道题目必须结合数位dp的方法,新建一个维度代表之后数字的大小是否受到限制。0代表受限,1代表不受限。但是处理前导零的部分的确较为头痛,最后采取的方法是在第一次dp的时候不允许有前导零的存在,在第二次dp的时候才把这一部分的答案统计出来。所以在第二次的dp转移中,一个节点只能由父亲节点、而不能由Fail节点转移而来。
#include <bits/stdc++.h> using namespace std; #define maxn 2000 #define mod 1000000007 int m, cnt = 1, len, ans, ch[maxn][10], dp[maxn][maxn][2], fail[maxn]; bool error[maxn]; string n, s; struct AC_Automation { void Trie_ins() { int now = 1, len = s.length(); for(int i = 0; i < len; i ++) { if(!ch[now][s[i] - '0']) ch[now][s[i] - '0'] = ++ cnt; now = ch[now][s[i] - '0']; } error[now] = true; } void AC_build() { queue <int> q; q.push(1); while(!q.empty()) { int u = q.front(); q.pop(); for(int i = 0; i < 10; i ++) { if(ch[u][i]) { int v = ch[u][i], k = fail[u]; while(!ch[k][i]) k = fail[k]; fail[v] = ch[k][i]; if(error[fail[v]]) error[v] = true; q.push(v); } else ch[u][i] = ch[fail[u]][i]; } } } }ACM; void DP(int x) { for(int i = 1; i <= cnt; i ++) { if(error[i] || (!dp[x - 1][i][0] && !dp[x - 1][i][1])) continue; for(int j = 0; j < 10; j ++) { if(x == 1 && j == 0) continue; int k = i; while(!ch[k][j]) k = fail[k]; int v = ch[k][j]; if(j == n[x - 1] - '0') dp[x][v][0] = (dp[x][v][0] + dp[x - 1][i][0]) % mod; if(j < n[x - 1] - '0') dp[x][v][1] = (dp[x][v][1] + dp[x - 1][i][0]) % mod; dp[x][v][1] = (dp[x][v][1] + dp[x - 1][i][1]) % mod; } } } void DP2() { dp[0][1][1] = 1; for(int x = 1; x < len; x ++) for(int i = 1; i <= cnt; i ++) { if(error[i] || !dp[x - 1][i][1]) continue; for(int j = 0; j < 10; j ++) { if(x == 1 && j == 0) continue; int k = i; int v = ch[k][j]; dp[x][v][1] = (dp[x][v][1] + dp[x - 1][i][1]) % mod; } } for(int i = 1; i < len; i ++) for(int j = 1; j <= cnt; j ++) if(!error[j]) ans = (ans + dp[i][j][1]) % mod; } int main() { cin >> n; cin >> m; for(int i = 0; i < 10; i ++) ch[0][i] = 1; for(int i = 1; i <= m; i ++) { cin >> s; ACM.Trie_ins(); } ACM.AC_build(); len = n.length(); dp[0][1][0] = 1; for(int i = 1; i <= len; i ++) DP(i); for(int i = 1; i <= cnt; i ++) if(!error[i]) ans = (ans + dp[len][i][0] + dp[len][i][1]) % mod; memset(dp, 0, sizeof(dp)); DP2(); printf("%d ", ans); return 0; }