【AC自動機】P3796 【模板】AC自動機(加強版)

題目鏈接:https://www.luogu.org/problem/P3796

這題相比簡單版就稍微調整一下,用ans數組存儲單詞出現的次數,然後最後比較次數大小輸出次數最多的那幾個字符串就行了,注意的是這題不用記錄點是否被訪問過,因爲要統計出現的次數。

#include<iostream>
#include<cstring>
#include<string>
#include<queue>
using namespace std;
const int maxn=1e6+10;
int cnt=0;
int ans[maxn];
int trie[maxn][26];//trie[i][j]表示的當前字母爲i+'a',且父節點編號爲i的節點的編號
int cntword[maxn];
int fail[maxn];
void insert(string s,int u)
{
	int root=0;
	for(int i=0;i<s.size();i++)
	{
		int next=s[i]-'a';
		if(!trie[root][next])
			trie[root][next]=++cnt;
		root=trie[root][next];
	}
	cntword[root]=u;//當前節點的單詞數+1,而不是等於1,因爲可能重複。
	//這時的root是單詞的末尾的編號,表示的是一整個單詞,途中經歷過的點的cntword是沒有自增的;
}
void getFail()//層序遍歷
{
	queue<int>q;
	for(int i=0;i<26;i++)
		if(trie[0][i])//首先要先將最上面一層出現的字母(每個串的首字符)入隊
		{
			fail[trie[0][i]]=0;
			q.push(trie[0][i]);
		}
	while(!q.empty())
	{
		int now=q.front();
		q.pop();
		for(int i=0;i<26;i++)
		{
			if(trie[now][i])//通過枚舉26個字母來找到now的子節點們
			{
				//該節點i的失敗指針指向它父節點now的失敗指針fail[now]的和i節點相同的節點
				fail[trie[now][i]]=trie[fail[now]][i];
				q.push(trie[now][i]);
			}
			else
				trie[now][i]=trie[fail[now]][i];//如果不存在這個節點就上跳到失配節點的等於i的這個子節點上
		}
	}
}
void query(string s)
{
	int now=0;
	for(int i=0;i<s.size();i++)
	{
		now=trie[now][s[i]-'a'];
		for(int j=now;j&&cntword[j]!=-1;j=fail[j])//當前節點匹配完成或失配時就上跳fail指針
		{
			ans[cntword[j]]++;//只有j是某個單詞末尾時纔有值
			//cntword[j]=-1;//記錄過的節點要標記,防止重複
		}
	}
}
int main()
{
	int n;
	while(cin>>n&&n)
	{
		cnt=0;
		memset(cntword,0,sizeof(cntword));
		memset(fail,0,sizeof(fail));
		memset(trie,0,sizeof(trie));
		memset(ans,0,sizeof(ans));
		string a[155],s;
		for(int i=1;i<=n;i++)
		{
			cin>>a[i];
			insert(a[i],i);
		}
		getFail();
		cin>>s;
		query(s);
		int maxnum=-0x7fffff;
		for(int i=1;i<=n;i++)
			maxnum=max(maxnum,ans[i]);
		cout<<maxnum<<endl;
		for(int i=1;i<=n;i++)
			if(ans[i]==maxnum)
				cout<<a[i]<<endl;
	}
	return 0;
}

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章