AC自动机
AC自动机是用于解决线性复杂度下 \(n\) 个字符串 \(s_{1},s_{2},\dots ,s_{n}\)在字符串\(t\)中的查询问题。
参考题目:
洛谷P3808【模板】AC 自动机(简单版)
洛谷P3796【模板】AC 自动机(加强版)
洛谷P5357【模板】AC 自动机(二次加强版)
算法思路:
- 首先对\(s_{1}\dots s_{n}\)建一棵trie树。
- 我们会发现两个字符串可能有重叠部分,可以极大地减少对比次数。
当一个结点的子结点失配时,它可以找到一个使它最大减少对比次数的结点(失配结点)继续匹配。
如何寻找?
假设此结点的父结点已经找到失配结点,从父结点的失配结点的子结点中寻找,若没有,再向父结点的失配结点的失配结点的子结点中寻找,以此类推,直到根结点。
但这样最劣$ \Theta (n^{2})$ ,考虑优化,每个结点都只有一个失配结点,所以将结点和失配结点建一条边得到一棵树。依此结点作为父结点时,一定在到根的这条链上。然后我们可以递推记录使得每次 $ \Theta (1)$操作。此纪录同样可以帮助之后的查询操作。 - 查询如果子结点有,就跳子结点,记录有,就跳记录,都没有就跳根节点。
在每个结点,走一遍失配链,更新答案。
这样可过加强版。
打标记就可过简单版。 - 但这依然过不了二次加强版,因为它在走失配链时还是$ \Theta (n^{2})$。这里可以发现树上的边都是指向父亲结点的有向边。这样就不用走失配链了,直接记录就可以了。
全都记录完后,树形DP就可以了。
简单版
#include<iostream>
#include<cstring>
using namespace std;
int n;
char s[1000010];
char t[1000010];
int l;
struct node{
int sp;
int zhi;
bool p;
int son[30];
}dian[1000010];
int tot=0;
void add(int i,int j){
if(j>l){
dian[i].zhi++;
return ;
}
if(dian[i].son[s[j]-'a'+1]==0){
dian[i].son[s[j]-'a'+1]=++tot;
}
add(dian[i].son[s[j]-'a'+1],j+1);
return ;
}
int dui[1000010],head=1,tail=0;
void bfs(){
dui[++tail]=0;
while(head<=tail){
int i=dui[head];
head++;
for(int j=1;j<=26;j++){
if(dian[i].son[j]!=0){
dui[++tail]=dian[i].son[j];
}
}
if(i==0){
for(int j=1;j<=26;j++){
if(dian[i].son[j]!=0){
dian[dian[i].son[j]].sp=0;
}
}
continue;
}
for(int j=1;j<=26;j++){
if(dian[i].son[j]!=0){
dian[dian[i].son[j]].sp=dian[dian[i].sp].son[j];
}
}
for(int j=1;j<=26;j++){
if(dian[i].son[j]==0){
dian[i].son[j]=dian[dian[i].sp].son[j];
}
}
}
}
int ans=0;
void work(){
int j=0;
for(int i=1;i<=l;i++){
int k=j;
while(k!=0&&dian[k].p==0){
dian[k].p=1;
ans+=dian[k].zhi;
dian[k].zhi=0;
k=dian[k].sp;
}
j=dian[j].son[t[i]-'a'+1];
}
int k=j;
while(k!=0&&dian[k].p==0){
dian[k].p=1;
ans+=dian[k].zhi;
dian[k].zhi=0;
k=dian[k].sp;
}
}
int main(){
cin>>n;
for(int i=1;i<=n;i++){
scanf("%s",s+1);
l=strlen(s+1);
add(0,1);
}
bfs();
scanf("%s",t+1);
l=strlen(t+1);
work();
cout<<ans;
return 0;
}
加强版
#include<iostream>
#include<cstring>
using namespace std;
int n;
char s[200][100];
char t[1000010];
int l;
struct node{
int zhi;
int sp;
int son[30];
int chuan[200];
int cnt;
}dian[20000];
int tot=0;
void add(int i,int j,int k){
if(j>l){
dian[i].zhi=1;
dian[i].chuan[++dian[i].cnt]=k;
return ;
}
if(dian[i].son[s[k][j]-'a'+1]==0){
dian[i].son[s[k][j]-'a'+1]=++tot;
}
add(dian[i].son[s[k][j]-'a'+1],j+1,k);
return ;
}
int dui[20000];
int head=1,tail=0;
void bfs(){
head=1,tail=0;
dui[++tail]=0;
while(head<=tail){
int i=dui[head];
head++;
for(int j=1;j<=26;j++){
if(dian[i].son[j]!=0){
dui[++tail]=dian[i].son[j];
}
}
if(i==0){
for(int j=1;j<=26;j++){
if(dian[i].son[j]!=0){
dian[dian[i].son[j]].sp=0;
}
}
continue;
}
for(int j=1;j<=26;j++){
if(dian[i].son[j]!=0){
dian[dian[i].son[j]].sp=dian[dian[i].sp].son[j];
}
else{
dian[i].son[j]=dian[dian[i].sp].son[j];
}
}
}
}
int tong[200];
void work(){
int j=0;
for(int i=1;i<=l;i++){
if(dian[j].son[t[i]-'a'+1]!=0){
j=dian[j].son[t[i]-'a'+1];
}
else{
j=0;
}
int k=j;
while(k!=0){
if(dian[k].cnt>0){
for(int l=1;l<=dian[k].cnt;l++){
tong[dian[k].chuan[dian[k].cnt]]++;
}
}
k=dian[k].sp;
}
}
}
int main(){
cin>>n;
while(n!=0){
for(int i=0;i<=tot;i++){
dian[i].zhi=0;
dian[i].sp=0;
dian[i].cnt=0;
for(int j=1;j<=26;j++){
dian[i].son[j]=0;
}
}
tot=0;
for(int i=1;i<=n;i++){
tong[i]=0;
scanf("%s",s[i]+1);
l=strlen(s[i]+1);
add(0,1,i);
}
bfs();
scanf("%s",t+1);
l=strlen(t+1);
work();
int ans=0;
for(int i=1;i<=n;i++){
ans=max(ans,tong[i]);
}
cout<<ans<<endl;
for(int i=1;i<=n;i++){
if(ans==tong[i]){
l=strlen(s[i]+1);
for(int j=1;j<=l;j++){
cout<<s[i][j];
}
cout<<endl;
}
}
cin>>n;
}
return 0;
}
二次加强版
#include<iostream>
#include<cstring>
#include<vector>
using namespace std;
int n;
char s[200010];
char t[2000010];
int l;
struct node{
int zhi;
int sp;
int son[30];
vector<int> chuan;
int cnt;
}dian[200010];
struct nod{
int to;
int nxt;
}edge[200010];
int hed[200010],tal;
void addedge(int u,int v){
edge[++tal].to=v;
edge[tal].nxt=hed[u];
hed[u]=tal;
}
int tot;
void add(int i,int j,int k){
if(j>l){
dian[i].cnt++;
dian[i].chuan.push_back(k);
return ;
}
if(dian[i].son[s[j]-'a'+1]==0){
dian[i].son[s[j]-'a'+1]=++tot;
dian[tot].chuan.push_back(0);
}
add(dian[i].son[s[j]-'a'+1],j+1,k);
return ;
}
int dui[200010],head=1,tail=0;
void bfs(){
dui[++tail]=0;
while(head<=tail){
int i=dui[head];
head++;
for(int j=1;j<=26;j++){
if(dian[i].son[j]!=0){
dui[++tail]=dian[i].son[j];
}
}
if(i==0){
for(int j=1;j<=26;j++){
if(dian[i].son[j]!=0){
dian[dian[i].son[j]].sp=0;
addedge(0,dian[i].son[j]);
}
}
continue;
}
for(int j=1;j<=26;j++){
if(dian[i].son[j]>0){
dian[dian[i].son[j]].sp=dian[dian[i].sp].son[j];
addedge(dian[dian[i].sp].son[j],dian[i].son[j]);
}
else{
dian[i].son[j]=dian[dian[i].sp].son[j];
}
}
}
}
int tong[200010];
void work(){
int j=0;
for(int i=1;i<=l;i++){
j=dian[j].son[t[i]-'a'+1];
dian[j].zhi++;
}
}
int dfs(int u){
int sum=dian[u].zhi;
for(int i=hed[u];i;i=edge[i].nxt){
int v=edge[i].to;
sum+=dfs(v);
}
for(int i=1;i<=dian[u].cnt;i++){
tong[dian[u].chuan[i]]=sum;
}
return sum;
}
int main(){
cin>>n;
for(int i=1;i<=n;i++){
scanf("%s",s+1);
l=strlen(s+1);
add(0,1,i);
}
bfs();
scanf("%s",t+1);
l=strlen(t+1);
work();
dfs(0);
for(int i=1;i<=n;i++){
cout<<tong[i]<<endl;
}
return 0;
}