KMP算法学习记录
参考:
https://blog.csdn.net/qq_62982856/article/details/128003067
https://blog.csdn.net/raelum/article/details/128823560
https://www.zhihu.com/question/21923021/answer/281346746
https://blog.csdn.net/qq_37969433/article/details/82947411
一、字符串基础
1️⃣ 字符串的前缀是指不包含最后一个字符的
所有以第一个字符(索引为0)开头的连续子串
比如字符串 “ABABA” 的前缀有:A,AB,ABA,ABAB
2️⃣ 字符串的后缀是指不包含第一个字符的
所有以最后一个字符结尾的连续子串
比如字符串 “ABABA” 的后缀有:BABA,ABA,BA,A
3️⃣ 公共前后缀:一个字符串的所有前缀连续子串
和所有后缀连续子串
中相等的子串
比如字符串 “ABABA”
- 前缀有:
A
,AB,ABA
,ABAB- 后缀有:BABA,
ABA
,BA,A
因此公共前后缀有:A ,ABA
4️⃣ 最长公共前后缀:所有公共前后缀的长度最长的 那个子串
比如字符串 “ABABA” ,公共前后缀有:
A ,ABA
由于 ABA 是 三个字符长度,A 是一个字符长度,那么最长公共前后缀就是 ABA
原文链接:https://blog.csdn.net/qq_62982856/article/details/128003067
二、KMP算法
构造 next 数组,保持原串的 i 指针不动,然后将匹配串的 j 指针指向模式字符串的 next[j] 位即可。
1. next 数组构建思想
next数组由 部分匹配表(Partial Match Table) 得到
我觉得理解KMP的最大障碍就是很多人在看了很多关于KMP的文章之后,仍然搞不懂PMT中的值代表了什么意思。这里我们抛开所有的枝枝蔓蔓,先来解释一下这个数据到底是什么。
对于字符串“abababca”
,它的PMT如下表所示:就像例子中所示的,如果待匹配的模式字符串有8个字符,那么PMT就会有8个值。
PMT中的值是字符串的前缀集合与后缀集合的交集中最长元素的长度。
例如,对于”aba”,它的前缀集合为{”a”, ”ab”},后缀集合为{”ba”,”a”}。两个集合的交集为{”a”},那么长度最长的元素就是字符串”a”了,长度为1,所以对于”aba”而言,它在PMT表中对应的值就是1
再比如,对于字符串”ababa”,它的前缀集合为{”a”, ”ab”,”aba”, ”abab”},它的后缀集合为{”baba”, ”aba”, ”ba”, ”a”}, 两个集合的交集为{”a”,”aba”},其中最长的元素为”aba”,长度为3。
如图所示,要在主字符串"ababababca"
中查找模式字符串"abababca"
。如果在 j 处字符不匹配,那么主字符串中 i 指针之前的 PMT[j −1] 位就一定与模式字符串的第 0 位至第 PMT[j−1] 位是相同的
。这是因为主字符串在 i 位失配,也就意味着主字符串从 i−j 到 i 这一段是与模式字符串的 0 到 j 这一段是完全相同的。
模式字符串从 0 到 j−1 ,在这个例子中就是”ababab”
,其前缀集合与后缀集合的交集的最长元素为”abab”
, 长度为4。所以就可以断言,主字符串中i指针之前的 4 位一定与模式字符串的第0位至第 4 位是相同的,即长度为 4 的后缀与前缀相同。这样一来,我们就可以将这些字符段的比较省略掉。具体的做法是,保持i指针不动,然后将j指针指向模式字符串的PMT[j −1]位即可。
以图中的例子来说,在 i 处失配,那么主字符串和模式字符串的前边6位就是相同的。又因为模式字符串的前6位,它的前4位前缀和后4位后缀是相同的,所以我们推知主字符串i之前的4位和模式字符串开头的4位是相同的。就是图中的灰色部分。那这部分就不用再比较了。
为了编程的方便, 我们不直接使用PMT数组,而是将PMT数组向后偏移一位。我们把新得到的这个数组称为
next数组
原文链接:https://www.zhihu.com/question/21923021/answer/281346746
2. next 数组的构建
假设有匹配串 aaabbab,我们来看看对应的 next 是如何被构建出来的。
最后,往原串和匹配串头部追加一个空格(哨兵)。目的是让 j 下标从 0 开始,省去 j 从 -1 开始的麻烦。第0位的值,我们将其设成了-1。
原文链接:https://leetcode.cn/problems/find-the-index-of-the-first-occurrence-in-a-string/solutions/575568/shua-chuan-lc-shuang-bai-po-su-jie-fa-km-tb86
举例
三、代码
求next数组值的程序如下所示:
void getNext(char * p, int * next)
{
next[0] = -1;
int i = 0, j = -1;
while (i < (int)strlen(p))
{
if (j == -1 || p[i] == p[j])
{
++i;
++j;
next[i] = j;
}
else
j = next[j];
}
}
具体KMP代码如下(示例):
nt KMP(char * t, char * p)
{
int i = 0;
int j = 0;
while (i < (int)strlen(t) && j < (int)strlen(p))
{
if (j == -1 || t[i] == p[j])
{
i++;
j++;
}
else
j = next[j];
}
if (j == strlen(p))
return i - j;
else
return -1;
}
原文链接:https://www.zhihu.com/question/21923021/answer/281346746