失配树,是一种奇妙的数据结构,它利用 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;
}