样例输入:
3 5
ab
bc
abc
acbcb
2 b
3 c
4 a
1 b
3 a
样例输出:
1
2
3
4
2
1
题目分析:
如果没有修改操作就是AC自动机模板题。记录n个字符串T中最长的长度为maxt,每次修改位置pos上的字符,其实它只会影响区间[pos-maxt,pos+maxt],所以先将原ans减去这段区间的贡献,再加上替换字符后这段区间的贡献。
附代码:
#include<iostream>
#include<cstdlib>
#include<cstdio>
#include<ctime>
#include<cmath>
#include<cstring>
#include<string>
#include<cctype>
#include<iomanip>
#include<algorithm>
using namespace std;
struct node{
int cnt;
int fail;
int son[26];
}trie[500010];
int t,tot,n,vst[500010],ans,m,pos,maxl,len,maxlen,start,end;
char s[100100],c;
int readint()
{
char ch;int i=0,f=1;
for(ch=getchar();(ch<'0'||ch>'9')&&ch!='-';ch=getchar());
if(ch=='-') {ch=getchar();f=-1;}
for(;ch>='0'&&ch<='9';ch=getchar()) i=(i<<3)+(i<<1)+ch-'0';
return i*f;
}
void create()//建trie树
{
int len=strlen(s+1);
if(len>maxl) maxl=len;
int u=1;
for(int i=1;i<=len;i++)
{
if(!trie[u].son[s[i]-'a'])
trie[u].son[s[i]-'a']=++tot;
u=trie[u].son[s[i]-'a'];
}
trie[u].cnt++;
}
void createfail()//建fail指针
{
static int qn,que[500010];
que[qn=1]=1;
for(int ql=1;ql<=qn;ql++)
{
int u=que[ql],v,w;
for(int i=0;i<26;i++)
{
v=trie[u].fail;
while(!trie[v].son[i])
v=trie[v].fail;
v=trie[v].son[i];w=trie[u].son[i];
if(w)
{
trie[w].fail=v;
que[++qn]=w;
trie[w].cnt+=trie[v].cnt;//这里加上其fail指针指向的位置的个数,在询问时就不用跳fail指针了,更节约时间
}
else
trie[u].son[i]=v;
}
}
}
int find()
{
int now=1;
int tmp;
int ans=0;
for(int i=start;i<=end;i++)
{
now=trie[now].son[s[i]-'a'];
ans+=trie[now].cnt;//因为上面的操作就不用跳fail指针,直接加就行
}
return ans;
}
int main()
{
//freopen("string.in","r",stdin);
//freopen("string.out","w",stdout);
for(int i=0;i<26;i++)
trie[0].son[i]=1;
n=readint();m=readint();
tot=1;
for(int i=1;i<=n;i++)
{
scanf("%s",s+1);
create();
}
createfail();
scanf("%s",s+1);
len=strlen(s+1);
start=1;end=len;
ans=find();
printf("%d\n",ans);
for(int i=1;i<=m;i++)
{
pos=readint();scanf("%c",&c);
start=max(pos-maxl,1);end=min(pos+maxl,len);
ans-=find();
s[pos]=c;
ans+=find();
cout<<ans<<endl;//printf("%d\n",ans);
}
return 0;
}