这是我参与2022首次更文挑战的第29天,活动详情查看:2022首次更文挑战
KMP算法
关于KMP算法,一般适用于字符串的部分匹配。
也就是我们的String的API —— indexOf()方法,但是区别在于一个是调库,一个是一种匹配的思路
理解过KMP算法后,意义在于脑海内会存在一个O(m+n)的API方便自己调用
带着实践去理解,会更容易思考。
例:
String a = "abcabcabf",String b = “abcabf”,求出字符串b出现在字符串a的首个下标
8.1 理解过程
首先,KMP需要分为2步,预处理前后缀 + 匹配字符串。
-
所谓的前后缀,我们把字符串 b 拆分成前缀集合和后缀集合,分别是{a,ab,abc,abca,abcab},和{bcabf,cabf,abf,bf,f},下面为部分匹配表的数组
char a b a b a b c a index 0 1 2 3 4 5 6 7 PMT 0 0 1 2 3 4 0 1 这也就是我们需要的预处理next数组,意义在于,每次匹配到不相符时,可以从上一个公共前后缀再出发,而不需要在从头来过
-
匹配字符串,就是从不匹配的部分,让其从数组中找出次大的下标重新出发
实现strStr()
该题出自力扣的28题 —— 实现strStr()【中等题】
这题也是典型的KMP练手题,没有任何胡里花俏
审题
实现 strStr() 函数。
给你两个字符串 haystack 和 needle ,请你在 haystack 字符串中找出 needle 字符串出现的第一个位置(下标从 0 开始)。如果不存在,则返回 -1 。
说明:
当 needle 是空字符串时,我们应当返回什么值呢?这是一个在面试中很好的问题。
对于本题而言,当 needle 是空字符串时我们应当返回 0 。这与 C 语言的 strstr() 以及 Java 的 indexOf() 定义相符
- 首先定义相应的字符长度 + 字符串转换字符数组
- 在字符串前 添加一个空格 作为哨兵下标,可以在后续遍历时不用考虑-1的情况
- 定义next数组
- 也就是上述所说的,找到最大前后缀交集,并把长度存于数组
- 也是使用双指针,如果不匹配则为初始值 0 ;
- 匹配字符串
- 本意在于,主字符串(haystack)不需要从头再来,只需要一次遍历就可以
- 如果匹配不成功,则下标回到当前的前后缀最大交集,也就是KMP的本意——不需要回到开头,省去一定成功的遍历
- 当然了,Java的String方法也是可以使用indexOf()方法实现的。
编码
public int strStr(String haystack, String needle) {
if(needle.isEmpty()) return 0;
int m = haystack.length(),n = needle.length();
haystack = " " + haystack;
needle = " " + needle;
char[] ss = haystack.toCharArray();
char[] pp = needle.toCharArray();
int[] next = new int[n+1];
//构建next数组,也就是 PTM
for(int i=0,j =2;j<=n;j++){
while(i>0 && pp[i+1] != pp[j])i = next[i];;
if(pp[i+1] == pp[j])i++;
next[j] = i;
}
for(int i=1,j = 0;i<= m;i++){
while(j >0 && ss[i] != pp[j+1])j = next[j];
if(ss[i] == pp[j+1])j++;
if(j == n)return i - n;
}
return -1;
}