对于回文树在线前后加入删除算法的更详细阐述和改进
感谢 Glacy 对于结论证明的贡献。
个人感觉论文里写的不太清楚,所以自己写一篇补充说明,包含自己解决这个问题的思想历程,以及对算法的一些细节上的改进。
插入没问题,跑非均摊的插入算法即可;删除有两个困难:求出当前串的最长回文前缀/后缀;判断回文树上的这个节点是否需要删除。下面挨个解决。
对于困难一,我们记
例如,假设当前的字符串是
有人可能会说,比较一下
继续修补以上的算法看上去没有前途:太混乱了。考虑我们实际需要的,其实是维护一些信息,满足插入删除时只会有
定义
注意到重要子串的性质:
设当前
在末尾插入
在末尾删除
于是插入删除时造成的修改都是
对于困难二,我们考虑类似只有一侧插入删除时的方法,对于回文树上每个节点,记录它成为了多少次前缀的最长回文后缀,或后缀的最长回文前缀,记为
我们只需要证明,对于任意字符串
该命题等价于,
这样,我们说明了
相比于论文中的算法,本算法无需使用哈希表维护
其实,我对论文中的算法是有疑问的,如果从左到右依次在末尾插入
本算法的代码实现(经过了对拍验证),其中
#include<bits/stdc++.h>
using namespace std;
constexpr int MN=1000005;
int a[MN*2],L=MN,R=MN-1,cnt=2,len[MN],fail[MN],tr[MN][26],fa[MN],tg[MN],qk[MN][26],lm[MN*2],rm[MN*2];
int main()
{
int q;
cin>>q;
memset(a,-1,sizeof a);
len[1]=-1,fail[1]=fail[2]=1;
for(int i=0;i<26;i++)
qk[1][i]=qk[2][i]=1;
for(int Q=1;Q<=q;Q++)
{
int o,c,xl,xr;
cin>>o;
if(L>R)
xl=xr=1;
else
xl=lm[L],xr=rm[R];
if(o==1)
{
cin>>c;
a[--L]=c;
if(a[L+len[xl]+1]!=c)
xl=qk[xl][c];
if(tr[xl][c])
xl=tr[xl][c];
else
{
tr[xl][c]=++cnt;
fa[cnt]=xl;
len[cnt]=len[xl]+2;
if(len[cnt]==1)
fail[cnt]=2;
else
fail[cnt]=tr[qk[xl][c]][c];
xl=cnt;
for(int i=0;i<26;i++)
qk[xl][i]=qk[fail[xl]][i];
qk[xl][a[L+len[fail[xl]]]]=fail[xl];
}
int r=L+len[xl]-1,t=fail[xl],l=r-len[t]+1;
lm[L]=rm[r]=xl;
if(len[xl]>1&&lm[l]==t)
lm[l]=0;
tg[xl]++;
}
else if(o==2)
{
cin>>c;
a[++R]=c;
if(a[R-len[xr]-1]!=c)
xr=qk[xr][c];
if(tr[xr][c])
xr=tr[xr][c];
else
{
tr[xr][c]=++cnt;
fa[cnt]=xr;
len[cnt]=len[xr]+2;
if(len[cnt]==1)
fail[cnt]=2;
else
fail[cnt]=tr[qk[xr][c]][c];
xr=cnt;
for(int i=0;i<26;i++)
qk[xr][i]=qk[fail[xr]][i];
qk[xr][a[R-len[fail[xr]]]]=fail[xr];
}
int l=R-len[xr]+1,t=fail[xr],r=l+len[t]-1;
rm[R]=lm[l]=xr;
if(len[xr]>1&&rm[r]==t)
rm[r]=0;
tg[xr]++;
}
else if(o==3)
{
int r=L+len[xl]-1,t=fail[xl],l=r-len[t]+1;
if(len[xl]>1&&len[lm[l]]<=len[t])
lm[l]=rm[r]=t;
else
rm[r]=0;
lm[L]=rm[L]=0;
if(!(--tg[xl]))
tr[fa[xl]][a[L]]=0;
a[L++]=-1;
}
else
{
int l=R-len[xr]+1,t=fail[xr],r=l+len[t]-1;
if(len[xr]>1&&len[rm[r]]<=len[t])
lm[l]=rm[r]=t;
else
lm[l]=0;
rm[R]=lm[R]=0;
if(!(--tg[xr]))
tr[fa[xr]][a[R]]=0;
a[R--]=-1;
}
}
return 0;
}