题目大意:
给定n个匹配串T,(|T|<=100),再给1个串S(|S|<=100000),问所有T共在S中出现了多少次。
再给m次操作pos,c,每次把S位置为pos的字母改为c(下标从一开始),每次操作后,问所有T共在S中出现了多少次。
n<=1000,m<=200000;
解题思路:
由于每个匹配串比较短,所以修改pos位置只会在[pos-|T|,pos+|T|]这个范围内的匹配改变,所以每次只用把这段区间放到AC自动机上去跑,减去这段区间的原贡献,加上改后的新贡献即可。
注意统计时不用一直跳fail指针,只用在建立fail的时候加上其所有fail链上的值即可。
#include<iostream>
#include<cstdio>
#include<cstring>
#include<string>
#include<algorithm>
using namespace std;
int getint()
{
int i=0,f=1;char c;
for(c=getchar();(c<'0'||c>'9')&&c!='-';c=getchar());
if(c=='-')c=getchar(),f=-1;
for(;c>='0'&&c<='9';c=getchar())i=(i<<3)+(i<<1)+c-'0';
return i*f;
}
int buf[1024];
inline void W(int x){
if(!x){putchar('0');return ;}
if(x<0){putchar('-');x=-x;}
while(x) buf[++buf[0]]=x%10,x/=10;
while(buf[0]) putchar(buf[buf[0]--]+48);
return ;
}
const int N=1005;
const int M=1e5+5;
const int L=105;
struct node
{
int cnt,fail,son[26];
}tr[N*L];
char s[M];
int tot,n,m,mx;
void insert()
{
int len=strlen(s),po=1;
mx=max(mx,len);
for(int i=0;i<len;i++)
{
if(!tr[po].son[s[i]-'a'])
tr[po].son[s[i]-'a']=++tot;
po=tr[po].son[s[i]-'a'];
}
tr[po].cnt++;
}
void buildfail()
{
int head=0,tail=1;
static int que[N*L];
que[tail]=1;
while(head<tail)
{
head++;
int u=que[head],v,w;
for(int i=0;i<26;i++)
{
v=tr[u].fail;
while(!tr[v].son[i])v=tr[v].fail;
v=tr[v].son[i],w=tr[u].son[i];
if(w)tr[w].fail=v,que[++tail]=w,tr[w].cnt+=tr[v].cnt;
else tr[u].son[i]=v;
}
}
}
int ac_auto(int l,int r)
{
int po=1,ans=0,tmp;
for(int i=l;i<=r;i++)
{
po=tr[po].son[s[i]-'a'];
ans+=tr[po].cnt;
}
return ans;
}
int main()
{
//freopen("string.in","r",stdin);
//freopen("string.out","w",stdout);
int pos;char c;
tot=1;
for(int i=0;i<26;i++)tr[0].son[i]=1;
n=getint(),m=getint();
while(n--)
scanf("%s",s),insert();
buildfail();
scanf("%s",s);
int len=strlen(s);
int ans=ac_auto(0,len-1);
W(ans),putchar('\n');
while(m--)
{
pos=getint();
pos--;
for(c=getchar();c<'a'||c>'z';c=getchar());
int l=max(0,pos-mx),r=min(len-1,pos+mx);
ans-=ac_auto(l,r);
s[pos]=c;
ans+=ac_auto(l,r);
W(ans),putchar('\n');
}
return 0;
}