題意:
給你n個單詞(字符串T)和一個文章(字符串S),q個詢問,每次修改S中的一個字符,然後輸出所有單詞在文章中的出現次數。一開始輸出沒有修改時的答案。
題解:
先將每個單詞建一個AC自動機,在沒有修改時統計一個答案;
然後我們發現,每次修改只會影響[pos-mx+1,pos+mx-1] (mx爲最長的單詞的長度,pos爲當前修改的位置)的答案,其餘多出的則不會影響;
所以每次修改時我們只需要先將原來區間內的答案刪去,修改之後在將這段區間放到AC自動機上匹配,加上新的答案;
另外還有注意普通的AC自動機在匹配時對於一個節點,會一直迭代,跳它的fail指針,統計答案,爲了避免超時,我們在一開始的時候就將一個節點所有fail指針跳的點的答案累加,後面匹配就不用迭代了
#include<cstdio>
#include<algorithm>
#include<cmath>
#include<ctime>
#include<iomanip>
#include<iostream>
#include<cstring>
#include<string>
#include<cctype>
using namespace std;
int ans,n,tot,qq,pos,mx,len;
int q[100010],tag[100010],fail[100010],f[100010][27];
char ch,s[200010];bool vis[100010];
inline void Insert(){
int t=0;
for(int i=1;i<=len;i++){
if(!f[t][s[i]-'a']) f[t][s[i]-'a']=++tot;
t=f[t][s[i]-'a'];
}
tag[t]++;
}
inline void getfail(){
int head=0,tail=1,j;q[1]=0;
while(head^tail){
j=q[++head];
if(j)
for(int i=0;i<26;i++)
if(f[j][i])
fail[f[j][i]]=f[fail[j]][i];
for(int i=0;i<26;i++)
if(!f[j][i])
f[j][i]=f[fail[j]][i];
else q[++tail]=f[j][i];
}
for(int i=1;i<=tail;i++)
{
int u=q[i],v=fail[u];
tag[u]+=tag[v];
}
}
inline int ac(int st,int end){
int now=0,tmp,ret=0;
for(int i=st;i<=end;i++){
now=f[now][s[i]-'a'];
ret+=tag[now];
}
return ret;
}
int main(){
// freopen("string.in","r",stdin);
scanf("%d%d",&n,&qq);
for(int i=1;i<=n;i++){
scanf("%s",s+1);
len=strlen(s+1);
mx=max(mx,len);
Insert();
}
getfail();
scanf("%s",s+1);len=strlen(s+1);
ans=ac(1,len);
cout<<ans<<endl;
for(int i=1;i<=qq;i++){
scanf("%d %c",&pos,&ch);
int l=pos-mx+1,r=pos+mx-1;
if(l<0) l=0; if(r>len) r=len;
ans-=ac(l,r);s[pos]=ch;
ans+=ac(l,r);
cout<<ans<<endl;
}
return 0;
}
後記:
由於這是我做的第三道AC自動機的題,所以模板不熟練,考試的時候就爆炸了,所以以後應該多做一些這樣的題