一.背景
给两个字符串 s1 s2 ,求s2在s1中是否出现过,出现过的话,给出在s1中的第一个索引。
二. 思路
首先根据s2维护一个前缀后缀表,比如:abbac : [-1, 0, 0, 0, 1]
然后在与s1进行比较,比如:
miabbabbac 与 abbac
- 首先有两个下标指针分别分
i, j;s1[i]与s2[j]
进行比较,不一样。 - 判断
j
能不能跳到自己维护的前缀表里的前缀,此时next[j] =-1
说明此时j就是第一个字符,说明第一个字符都匹配不上,那么i++
,也就是从s2
的第二个字符开始判断 s2[I] == s1[j]
发现不相等,然后判断j有没有前缀可以跳,也就是next[j] == -1
发现跳不了,那么继续i++
从s1
的第三个字符开判断j
跳到next[j]
然后判断s1[i] == s2[j]
,相等了,那么s1
与s2
同时i++ j++
判断s1[i] == s2[j]
,还是相等,那么继续i++ j++
,然后判断b==b
,还是相等,i++ j++
发现a == a,
继续i++ j++
发现b!=c
此时不相等- 然后因为此时
c
之前的字符串已经判断完了,都是相同的,那么我们就利用这个信息也就是从j = next[j]
开始判断s1[i] == s2[j]也就是b == s2[1] (也就是b)
,相等,然后继续i++ j++
- 最后发现
i
走过的路>j
j
走过的就是s2
的长度 起始位置就是i - j
判断方法就是j == s2.length()
三. 例题
【leetcode 28】
public class Kmp {
public static int strStr(String haystack, String needle) {
if(needle == "" || needle.length() == 0) {
return 0;
}
if(haystack == "" || needle.length() > haystack.length()){
return -1;
}
int i1 = 0;
int i2 = 0;
int[] next = getNext(needle);
while(i1 < haystack.length() && i2 < needle.length()){
if (haystack.charAt(i1) == needle.charAt(i2)) {
i1++;
i2++;
}else {
if(next[i2] == -1){
i1++;
}else{
i2 = next[i2];
}
}
}
return i2 == needle.length() ? i1 - i2 : -1;
}
private static int[] getNext(String str) {
if(str == "" || str.length() == 0){
return null;
}
int[] next = new int[str.length()];
if (str.length() == 1){
next[0] = -1;
return next;
}
if (str.length() == 2){
next[0] = -1;
next[1] = 0;
return next;
}
int i = 2;
next[0] = -1;
next[1] = 0;
int cn = 0;
while(i < str.length()){
if (str.charAt(i-1) == str.charAt(cn)){
next[i++] = ++cn;
}else if(cn > 0){
cn = next[cn];
}else {
next[i++] = 0;
}
}
print(next);
return next;
}
public static void print(int[] a){
for (int i : a) {
System.out.print(i + " ");
}
System.out.println();
}
public static void main(String[] args) {
String str1 = "mississippi";
String str2 = "issip";
int i = strStr(str1, str2);
System.out.println(i);
}
}