失配树,是一种奇妙的数据结构,它利用 KMP、LCA 解决求两前缀的最长公共 Border 的问题。
首先介绍一下什么是 Border,我们知道 nxt
数组是前后缀相同的最大长度,Border 相当于是 nxt
数组的弱化版,只是去掉了“最大”的限制。
我们考虑如何建立一棵失配树(fail 树),对于每一个长度为
i
i
i 的前缀,我们预处理出它的 nxt
,然后按照
i
i
i 指向 nxt[i]
,即 nxt[i]
是
i
i
i 的爹。
对于两个前缀的最长 Border,我们只需要对于两个区间的
i
i
i、
j
j
j 求出它们的 LCA 即可。这里需要注意一个坑,如果
i
i
i 和
j
j
j 的 LCA 是他们中的一个,那么我们要把 LCA 上提一步,即返回 f[i][0]
或 f[j][0]
(返回他们的父亲)。
代码如下:
#include <bits/stdc++.h>
using namespace std;
const int maxn=1e6+5;
char s[maxn];
int f[maxn][25],dep[maxn];
int lca(int x,int y)
{
if(dep[x]<dep[y]) swap(x,y);
for(int i=20;i>=0;i--) if(dep[f[x][i]]>=dep[y]) x=f[x][i];
if(x==y) return f[x][0];
for(int i=20;i>=0;i--)
if(f[x][i]!=f[y][i])
x=f[x][i],y=f[y][i];
return f[x][0];
}
int main()
{
scanf("%s",s+1);
int len=strlen(s+1);
f[0][0]=f[1][0]=0;dep[0]=0;dep[1]=1;
for(int i=1,j=0;i<=len;i++)
{
while(j&&s[i+1]!=s[j+1]) j=f[j][0];
if(s[i+1]==s[j+1]) j++;
f[i+1][0]=j,dep[i+1]=dep[j]+1;
}
int m;cin>>m;
for(int j=1;j<=20;j++) for(int i=1;i<=len;i++) f[i][j]=f[f[i][j-1]][j-1];
while(m--)
{
int p,q;cin>>p>>q;
cout<<lca(p,q)<<endl;
}
return 0;
}