[复习] border 与 单模式匹配 KMP 算法

[复习] KMP

前缀函数

\(S_i\) 为字符串 \(S\) 的第 \(i\) 个位置。

我们设 \(\pi(i)\) 表示字符串以 \(i\) 结尾的前缀的最长公共前后缀的长度,也记作 border

这里的前后缀都指的是真前缀、真后缀。

怎么 \(O(n)\) 求出 \(\pi(i)\)

性质:相邻的 \(\pi\) 至多增加 1。

因此, 若 \(s[\pi(i)+1]=s[i+1]\) 时,\(\pi(i+1)=\pi(i)+1\)

如果失配,我们要找到 \(i\) 前缀的第二长的公共前后缀,然后再次比较。以此类推。

那么

\[[s_1s_2s_3s_4]...[s_{i-3}s_{i-2}s_{i-1}s_{i}]s_{i+1} \]

如果 \(\pi(i)=4\),即 \(s[1...4]=s[i-3...i]\)

而第二长的公共前后缀长度 \(j\),假如 \(j=2\),那么 \(s[1...2]=s[i-1...i]\)

由于 \(s[1...4]=s[i-3...i]\),那么 \(s[i-1...i]=s[3...4]\),所以 \(s[1...2]=s[3...4]\)

所以第二长的公共前后缀长度是前缀 \(\pi(i)\) 的最长公共前后缀长度,即 \(\pi(\pi(i))\)

所以如果 \(\pi(i)+1\) 失配,那么就找到 \(\pi(\pi(i))+1\),然后是 \(\pi(\pi(\pi(i)))+1\dots\)

到最后找到 \(1\),若不相等则 \(\pi(i+1)=0\)

核心代码:

nx[1]=0;
for(int i=2;i<=len;i++){
	int j=nx[i-1];
	while(j>0&&s[j+1]!=s[i])j=nx[j];
	if(s[i]==s[j+1])++j;
	nx[i]=j;
}

KMP 算法

对于文本串 \(s\) 和模式串 \(t\) 匹配,可以求出字符串 \(t+\#+s\) 的前缀函数,其中 \(\#\) 是一个额外字符,然后就能知道 \(s\) 每个前缀能否与 \(t\) 匹配。

fail 树

AC 自动机中的定义和这里类似。

我们把 \(\pi(i)\) 连向 \(i\),这样就得到一棵以 \(0\) 为根的树,称作 \(fail\) 树。

性质:显然的,在 \(fail\) 树上,一个点 \(i\) 的所有祖先都是 \(i\)\(border\)
或者说,\(j\)\(i\)\(border\) 当且仅当 \(j\)\(i\) 的祖先。

最长公共 border

对于两个前缀 \(i,j(i\ne j)\),它们的最长公共 \(border\),就是 \(i\)\(j\)\(fail\) 树上的 \(LCA\)

posted @ 2024-10-19 19:57  dengchengyu  阅读(4)  评论(0编辑  收藏  举报