[题解]lgP4052文本生成器
[题解]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神%%%