刷题总结——字符串KMP算法分析与应用
本文最后更新于:2021年7月28日 下午
刷题总结——字符串的KMP算法分析与应用
概述
- KMP的经典思想就是:当出现字符串不匹配时,可以记录一部分之前已经匹配的文本内容,利用这些信息避免从头再去做匹配。
- 如何记录已经匹配的文本内容,是KMP的重点,也是next数组肩负的重任
- next数组:前缀表(prefix table)
- 前缀表是用来回退的,它记录了模式串(与主串(文本串)不匹配的时候,模式串应该从哪里开始重新匹配
- 记录下标i之前(包括i)的字符串中,有多大长度的相同前缀后缀
- 最长相等前后缀
- 前缀:指不包含最后一个字符的所有以第一个字符开头的连续子串
- 后缀:指不包含第一个字符的所有以最后一个字符结尾的连续子串
- 前缀表要求的就是相同前后缀的长度
如何构造前缀表和next数组
-
前缀表中记录了模式串中每个位置从 [0,位置] 的最长相等前后缀的长度
- 前缀表的长度等于模式串
-
当在 模式表i 处发生不匹配时,只需找到前缀表中i-1的位置处的值,即可知道下次匹配从
模式表[前缀表[i-1]]
处开始 -
为了简便,通常会将前缀表向右移动并赋初值为-1,得到
next数组
,这样下次匹配就是模式表[next[i]]
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21/*构造nextu
1. 初始化
2. 处理前后缀不同的情况
3. 处理前后缀相同的情况
整个算法有点动态规划的味道,在前一段完成的基础上,获得下一段的情况
*/
void getNext(int* next, const string& s) {
next[0] = -1; //先给next[0]赋初值,必定是-1
int j = 0;
int k=-1; //记录s[0]-s[j-1]最长相等前缀的后一个位置
while(j<s.length()-1){ //因为我们是使用右移的next数字,所以循环的j实际上是给j+1赋值
if(k==-1||s[j]==s[k]){ //这里加k==-1这个条件,实际上也是一个给next[1]赋初值的条件,因为s[1]不匹配的话,只能用s[0]l来匹配,
//如果s[j+1]不匹配,考虑0-j的字符串,当s[k]==s[j]时,0-j字符串的k应该是k+1,所以s[j+1]=k+1(之后为了下次循环,k,j也要往前移动一位)
j++;k++;
next[j]=k;
}else{
//如果s[j+1]不匹配时,考虑0-j的字符串,当s[j]!=s[k],0-j的最长相等前缀位置不是k(因为k是0-j-1的),所以要更新这次的k。因为k是0-j-1的最长相等前缀的后一个位置,所以s[j]!=s[k]最长前缀只可能在0-k-1中出现,那么next[k]就应该是下一个去匹配的位置,进入下一次循环判断直到有s[k]==s[j],那么此时的k就是0-j处的最长前缀的后一个位置
k=next[k];
}
}
}
题目练习
在一个串中查找是否出现过另一个串
-
-
next数组可以优化
1
next[j]=s[j]!=s[k]?k:next[k]//next数组优化,避免s[j+1]发生不匹配时,下一个s[next[j+1]]==s[j+1]继续不匹配,需要继续往下匹配
-
扩展阅读
本博客所有文章除特别声明外,均采用 CC BY-SA 4.0 协议 ,转载请注明出处!