References
Refer to http://www.zhenganwen.top/articles/2018/10/13/1565048872487.html
Refer to 牛客算法进阶班 (第一章 KMP算法和Manacher算法详解2018.10.14)
KMP算法
1. 原始问题
对于一个字符串str(长度为N)和另一个字符串match(长度为M),如果match是str的子串,请返回其在str第一次出现时的首字母下标,若match不是str的子串则返回-1(Leetcode)。
2. Brute Force - O(nm) runtime, O(1) space
最简单的方法是将str从头开始遍历并与match逐次比较,若碰到了不匹配字母则终止此次遍历转而从str的第二个字符开始遍历并与match逐次比较,直到某一次的遍历每个字符都与match匹配否则返回-1。
public int strStr(String str, String match) {
for (int i = 0; ; i++) {
for (int j = 0; ; j++) {
if (j == match.length()) return i;
if (i + j == str.length()) return -1;
if (match.charAt(j) != str.charAt(i + j)) break;
}
}
}
3. 详细算法走起来
3.1 引入概念
前后缀子串最大匹配长度
- 前缀子串表示一个串中以串首字符开头的不包含串尾字符的任意个连续字符
- 后缀子串表示一个串中以串尾字符结尾的不包括串首字符的任意个连续字符
- 比如abcd的前缀子串可以是a、ab、abc,但不能是abcd,而abcd的后缀字串可以是d、cd、bcd,但不能是abcd。
KMP算法需要对应match创建一个与match长度相同的辅助数组nextAll[match.length],该数组元素表示match某个下标之前的子串的前后缀子串最大匹配长度。对于char match[]="abc1abc2"来说,
match | a | b | c | 1 | a | b | c | 2 |
---|---|---|---|---|---|---|---|---|
index | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 |
nextAll | -1 | 0 | 0 | 0 | 0 | 1 | 2 | 3 |
有match[7]=3,因为match[7]=‘2’,因此match下标在7之前的子串abc1abc的前缀子串和后缀子串相同的情况下,前缀子串的最大长度为3(即前缀字串和后缀字串都取abc);又如match=“aaaab”,有nextAll[4]=3(前缀子串和后缀子串最大匹配长度当两者为aaa时取得),相应的有nextAll [3]=2、nextAll [2]=1。
match | a | a | a | a | b |
---|---|---|---|---|---|
index | 0 | 1 | 2 | 3 | 4 | </