题目大意:
开始时给一个空字符串,要求支持在首尾加字符,询问本质不同的回文串个数和所有回文串个数。
解题思路:
很容易想到修改一下回文树的写法,从中间向两边拓展节点,本质不同的回文串个数就是回文树节点个数,而每加一个字母,增加的回文串个数就是num[last]。
注意到我们用了两个指针last[0],last[1]维护最长回文前(后)缀,所以在首(尾)添加字符后如果整个串变成了回文串,last[0]、last[1]都会变化为同一个节点。
#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int N=200005;
int n;
struct PAM
{
int tot,last[2],l[2],s[N],len[N],num[N],fail[N],son[N][26];
inline int newnode(int l)
{
len[++tot]=l;
return tot;
}
inline void clear()
{
memset(son,0,sizeof(son));
memset(fail,0,sizeof(fail));
memset(num,0,sizeof(num));
memset(len,0,sizeof(len));
tot=-1,last[0]=last[1]=0,l[1]=n,l[0]=n+1;
newnode(0),newnode(-1),fail[0]=1,fail[1]=0;
}
inline int get_fail(int x,int t)
{
s[l[0]-1]=s[l[1]+1]=-1;
while(s[l[t]]!=s[l[t]+(t?-1:1)*(len[x]+1)])
x=fail[x];
return x;
}
inline int add(int c,int t)
{
s[l[t]+=(t?1:-1)]=c;
int cur=get_fail(last[t],t);
if(!son[cur][c])
{
int now=newnode(len[cur]+2);
fail[now]=son[get_fail(fail[cur],t)][c];
son[cur][c]=now;
}
last[t]=son[cur][c],num[last[t]]=num[fail[last[t]]]+1;
if(l[1]-l[0]+1==len[last[t]])last[t^1]=last[t];
return num[last[t]];
}
}pam;
int main()
{
//freopen("lx.in","r",stdin);
while(scanf("%d",&n)!=EOF)
{
pam.clear();
int op;ll ans=0;char s[2];
for(int i=1;i<=n;i++)
{
scanf("%d",&op);
if(op==1)scanf("%s",s),ans+=pam.add(s[0]-'a',0);
else if(op==2)scanf("%s",s),ans+=pam.add(s[0]-'a',1);
else if(op==3)printf("%d\n",pam.tot-1);
else printf("%lld\n",ans);
}
}
return 0;
}