题面
大概就是用栈维护
logn
个AC自动机,每次新加入一个单词就新建一个AC自动机,当栈顶的自动机和栈顶下一个自动机包含单词个数相同的时候就暴力重构合并。
一个单词最多被合并
log
次,然后查询的时候也只会跑
log
个自动机,所以复杂度是
O(nlog|t|+|s|logn)
的。其实还带个
26
的常数,不过跑得挺快的。。。
代码:
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#define ll long long
#define N 1000010
using namespace std;
int n,type,cnt,s[N],c[N],a[N][26],b[N][26],fa[N],w[N],bw[N],dl[N],top;
char cs[N];
struct AC
{
int root,size;
void init(int *s,int len,int rw)
{
root=++cnt;
for(int i=1;i<=len;i++)
a[cnt][s[i]]=++cnt;
w[cnt]=rw;
size=1;
}
void clr()
{
for(int i=cnt;i>=root;i--)
{
fa[i]=w[i]=c[i]=0;
memset(a[i],0,sizeof(a[i]));
}
cnt=root-1;
}
void getac()
{
dl[1]=root;fa[root]=root;
for(int hd=1,tl=1,v=root;hd<=tl;v=dl[++hd])
for(int ic=0;ic<=25;ic++)
if(a[v][ic])
{
int p=fa[v],q=a[v][ic];
while(p!=root&&!a[p][ic]) p=fa[p];
if(a[p][ic]&&a[p][ic]!=q) fa[q]=a[p][ic];else fa[q]=root;
c[q]=c[fa[q]]+w[q];
dl[++tl]=q;
}
}
ll run(int *s,int len)
{
ll re=0;
for(int i=1,p=root;i<=len;i++)
{
while(p!=root&&!a[p][s[i]]) p=fa[p];
if(a[p][s[i]]) p=a[p][s[i]];
re+=c[p];
}
return re;
}
}Tac[23];
void merge(int v,int u)
{
w[v]+=bw[u];
for(int ic=0;ic<=25;ic++)
if(b[u][ic])
{
if(!a[v][ic]) a[v][ic]=++cnt;
merge(a[v][ic],b[u][ic]);
}
}
int main()
{
scanf("%d%d",&n,&type);
ll ans=0;
for(int i=1;i<=n;i++)
{
int opt,len;
scanf("%d%s",&opt,cs+1);
len=strlen(cs+1);
for(int i=1;i<=len;i++)
s[i]=((cs[i]-'a')^(type*ans))%26;
if(opt)
{
ans=0;
for(int i=1;i<=top;i++)
ans+=Tac[i].run(s,len);
printf("%lld\n",ans);
}
else
{
int rw;
scanf("%d",&rw);
Tac[++top].init(s,len,rw);
while(top>1&&Tac[top].size==Tac[top-1].size)
{
for(int i=cnt;i>=Tac[top].root;i--)
memcpy(b[i],a[i],sizeof(b[i])),bw[i]=w[i];
Tac[top-1].size+=Tac[top].size;
Tac[top--].clr();
merge(Tac[top].root,cnt+1);
}
Tac[top].getac();
}
}
return 0;
}