1. 程式人生 > 實用技巧 >[題解]lgP4052文字生成器

[題解]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神%%%