肝了好几天,太抽象,理解还需靠题目慢慢加深
字典树和KMP的思想相结合的产物
核心在于跳fail指针
匹配完一个字符,跳向父节点fail指针指向的儿子结点,若为该单词结尾,则计数
P3808 【模板】AC自动机(简单版)
题目描述
给定 nn 个模式串 s_is
i
和一个文本串 tt,求有多少个不同的模式串在文本串里出现过。
两个模式串不同当且仅当他们编号不同。
输入格式
第一行是一个整数,表示模式串的个数 nn。
第 22 到第 (n + 1)(n+1) 行,每行一个字符串,第 (i + 1)(i+1) 行的字符串表示编号为 ii 的模式串 s_is
i
。
最后一行是一个字符串,表示文本串 tt。
输出格式
输出一行一个整数表示答案。
#include<bits/stdc++.h>
using namespace std;
#define ll long long
struct node{
int son[26],tot,fail;
}tree[1000002];
int n,ans,cnt;
string s;
queue<int> q;
void add_word(string s){
int l=s.size(),now=0;
for(int i=0;i<l;i++){
if(!tree[now].son[s[i]-'a']){
tree[now].son[s[i]-'a']=++cnt;
}
now=tree[now].son[s[i]-'a'];
}
tree[now].tot++;
}
void buil(){
for(int i=0;i<26;i++){
if(tree[0].son[i]){
q.push(tree[0].son[i]);
tree[tree[0].son[i]].fail=0;
}
}
while(!q.empty()){
int t=q.front();q.pop();
for(int i=0;i<26;i++){
if(tree[t].son[i]){
tree[tree[t].son[i]].fail=tree[tree[t].fail].son[i];
q.push(tree[t].son[i]);
}
else{
tree[t].son[i]=tree[tree[t].fail].son[i];
}
}
}
}
void query(string s){
int now=0,l=s.size();
for(int i=0;i<l;i++){
int j=tree[now].son[s[i]-'a'];
while(j&&tree[j].tot!=-1){
ans+=tree[j].tot;
tree[j].tot=-1;
j=tree[j].fail;
}
now=tree[now].son[s[i]-'a'];
}
}
int main(){
ios::sync_with_stdio(0);
cin>>n;
for(int i=1;i<=n;i++){
cin>>s;
add_word(s);
}
cin>>s;
buil();
query(s);
cout<<ans<<endl;
return 0;
}
P3796 【模板】AC自动机(加强版)
有 NN 个由小写字母组成的模式串以及一个文本串 TT。每个模式串可能会在文本串中出现多次。你需要找出哪些模式串在文本串 TT 中出现的次数最多。
输入格式
输入含多组数据。保证输入数据不超过 5050 组。
每组数据的第一行为一个正整数 NN,表示共有 NN 个模式串,1 \leq N \leq 1501≤N≤150。
接下去 NN 行,每行一个长度小于等于 7070 的模式串。下一行是一个长度小于等于 10^610
6
的文本串 TT。保证不存在两个相同的模式串。
输入结束标志为 N=0N=0。
输出格式
对于每组数据,第一行输出模式串最多出现的次数,接下去若干行每行输出一个出现次数最多的模式串,按输入顺序排列。
#include<bits/stdc++.h>
using namespace std;
#define int long long
int n,tot,res[500005];
string s[155],b;
struct node{
int son[26],fail,end;
}tree[500005];
void add_word(string s,int pos){
int l=s.size(),now=0;
for(int i=0;i<l;i++){
if(!tree[now].son[s[i]-'a']){
tree[now].son[s[i]-'a']=++tot;
}
now=tree[now].son[s[i]-'a'];
}
tree[now].end=pos;
}
void build_fail(){
queue<int> q;
for(int i=0;i<26;i++){
if(tree[0].son[i]){
q.push(tree[0].son[i]);
tree[tree[0].son[i]].fail=0;
}
}
while(!q.empty()){
int t=q.front();q.pop();
for(int i=0;i<26;i++){
if(tree[t].son[i]){
tree[tree[t].son[i]].fail=tree[tree[t].fail].son[i];
q.push(tree[t].son[i]);
}
else{
tree[t].son[i]=tree[tree[t].fail].son[i];
}
}
}
}
void query(string s){
int l=s.size(),now=0;
for(int i=0;i<l;i++){
int j=tree[now].son[s[i]-'a'];
while(j){
res[tree[j].end]++;
j=tree[j].fail;
}
now=tree[now].son[s[i]-'a'];
}
}
signed main(){
ios::sync_with_stdio(0);
while(cin>>n,n){
tot=0;
memset(tree,0,sizeof(tree));
memset(res,0,sizeof(res));
for(int i=1;i<=n;i++){
cin>>s[i];
add_word(s[i],i);
}
cin>>b;
build_fail();
query(b);
int maxl=-1;
for(int i=1;i<=n;i++){
maxl=max(res[i],maxl);
}
cout<<maxl<<endl;
for(int i=1;i<=n;i++){
if(res[i]==maxl){
cout<<s[i]<<endl;
}
}
}
return 0;
}