[題解]lgP4052文字生成器
阿新 • • 發佈:2020-10-26
[題解]lgP4052文字生成器
問題分析
這個題是要求多模式串的匹配,所以自然的想到AC自動機,但是題目中說"至少含有一個認識的單詞"並不好處理,所以我們不妨求出所有不存在認識的單詞的文字串,再用總的可能情況減去不含有認識單詞的文字串數量即可.
既然要求不存在認識單詞的文字串,那麼匹配過程中肯定不能拓展到存在有單詞的節點(可以稱之為結束節點)(在程式中的標誌是trie[now].cnt != 0
,意思就是如果擴充套件到當前節點,那麼就存在有認識的單詞,可以結合cnt的定義來理解,參見AC自動機部落格),所以一切可以到達結束節點的方案都是存在有認識的單詞的,所以在求\(fail\)指標的時候可以進行標記.
在統計答案的時候要用\(dp\),設\(dp[i][j]\)表示從根節點走\(i\)步到達\(j\)節點的方案數,轉移方程:
\(dp[i][j] = Σ_{t[k].cnt == 0}{dp[i - 1][k]}\)
程式碼如下
#include <bits/stdc++.h> using namespace std; int n,m; struct node{ int fail; int cnt; int next[62]; }trie[1000010 * 3]; char s[6100]; int tot; void build(int id,char s[]){ int len = strlen(s); for(int i = 0;i < len;i++){ int tmp = s[i] - 'A'; if(trie[id].next[tmp] == 0){ trie[id].next[tmp] = ++tot; } id = trie[id].next[tmp]; } trie[id].cnt = 1; } void build_fail(int id){ queue < int > q; while(!q.empty())q.pop(); for(int i = 0;i < 26;i++){ if(trie[id].next[i] != 0){ trie[trie[id].next[i]].fail = id; q.push(trie[id].next[i]); } } while(!q.empty()){ int x = q.front(); q.pop(); for(int i = 0;i < 26;i++){ if(trie[x].next[i] == 0){ trie[x].next[i] = trie[trie[x].fail].next[i]; continue; } else { trie[trie[x].next[i]].fail = trie[trie[x].fail].next[i]; q.push(trie[x].next[i]); trie[trie[x].next[i]].cnt += trie[trie[trie[x].next[i]].fail].cnt; } } } } int f[6110][6110]; int ans; void dp(){ f[0][0] = 1; for(int i = 1;i <= m;i++){ for(int j = 0;j <= tot;j++){ if(!trie[j].cnt){ for(int k = 0;k < 26;k++){ f[i][trie[j].next[k]] = (f[i][trie[j].next[k]] + f[i - 1][j]) % 10007; } } } } for(int i = 0;i <= tot;++i){ if(!trie[i].cnt){ ans += f[m][i]; ans %= 10007; } } } int pow(int x,int t){ if(t == 1)return x; if(t == 0)return 1; if(t & 1)return ((x * pow(x,t >> 1)) % 10007 * pow(x,t >> 1)) % 10007; else return (pow(x,t >> 1) * pow(x,t >> 1)) % 10007; } int main(){ scanf("%d%d",&n,&m); for(int i = 1;i <= n;i++){ cin>>s; build(0,s); } build_fail(0); dp(); cout<<(pow(26,m) - ans + 10007) % 10007<<endl; return 0; }
參考了Wen佬的部落格
Wen神%%%