今天了解了一下KMP算法,确实有一些晦涩难懂,感觉思路方面是没什么问题了,但是对于KMP算法的代码实现,还是有一些难理解的。
LeetCode 28 找出字符串中第一个匹配项的下标
题目链接:28.找出字符串中第一个匹配项的下标
本题是基础KMP算法的一个引子,本题的核心就是实现KMP算法,因此思路部分我主要记录的就是我对于KMP算法的理解,代码步骤以及实现部分我也主要记录的是如何实现KMP算法,只要实现了KMP算法,那么这题就迎刃而解了。
【解题思路】
KMP算法主要是应用在字符串的匹配上,其主要的思想是:
当前出现字符串不匹配时,可以知道一部分之前已经匹配的文本内容,可以利用这些信息避免从头再去做匹配。
前缀表是KMP算法的核心
前缀表的作用是用来回退的,它记录了模式串与主串(文本串)不匹配的时候,模式串应该从哪里开始重新匹配。
最长公共前后缀
最长公共前后缀其实就是最长相等前后缀,而前缀表的要求就是相同前后缀的长度!
前缀是指不包含最后一个字符的所有以第一个字符开头的连续子串。
后缀是指不包含第一个字符的所有以最后一个字符结尾的连续子串。
【解题步骤】
如何构造next数组
1.初始化:
定义两个指针i和j,j指向前缀末尾位置,i指向后缀末尾位置,然后再对next数组初始化。
2.处理前后缀不相同的情况
1)因为j初始化为-1,那么i就从1开始,进行s[i]与s[j+1]的比较,所以遍历模式串s的循环下标i要从1开始
2)如果s[i]与s[j+1]不相同,也就是遇到前后缀末尾不相同的情况,就向前回退。
3.处理前后缀相同的情况
如果s[i]与s[j+1]相同,那么i和j同时向后移动。
3.更新next数组的值。
【代码部分】
java:
class Solution {
public void getNext(int[] next, String s){
int j = -1;
next[0] = j;
for (int i = 1; i < s.length(); i++){
while(j >= 0 && s.charAt(i) != s.charAt(j+1)){
j=next[j];
}
if(s.charAt(i) == s.charAt(j+1)){
j++;
}
next[i] = j;
}
}
public int strStr(String haystack, String needle) {
if(needle.length()==0){
return 0;
}
int[] next = new int[needle.length()];
getNext(next, needle);
int j = -1;
for(int i = 0; i < haystack.length(); i++){
while(j>=0 && haystack.charAt(i) != needle.charAt(j+1)){
j = next[j];
}
if(haystack.charAt(i) == needle.charAt(j+1)){
j++;
}
if(j == needle.length()-1){
return (i-needle.length()+1);
}
}
return -1;
}
}
LeetCode 459 重复的子字符串
题目链接:459.重复的子字符串
【解题思路】
因为KMP算法的作用就是在一个串中查找是否出现过另一个串,因此此题可以用KMP解,而在最长相等前缀与最长相等后缀中没包含的子字符串就是我们要求的最小重复子字符串。
【代码部分】
java:
public boolean repeatedSubstringPattern(String s) {
if (s.equals("")) return false;
int len = s.length();
// 原串加个空格(哨兵),使下标从1开始,这样j从0开始,也不用初始化了
s = " " + s;
char[] chars = s.toCharArray();
int[] next = new int[len + 1];
// 构造 next 数组过程,j从0开始(空格),i从2开始
for (int i = 2, j = 0; i <= len; i++) {
// 匹配不成功,j回到前一位置 next 数组所对应的值
while (j > 0 && chars[i] != chars[j + 1]) j = next[j];
// 匹配成功,j往后移
if (chars[i] == chars[j + 1]) j++;
// 更新 next 数组的值
next[i] = j;
}
// 最后判断是否是重复的子字符串,这里 next[len] 即代表next数组末尾的值
if (next[len] > 0 && len % (len - next[len]) == 0) {
return true;
}
return false;
}