仅作个人总结,初学KMP不建议看本文
1.暴力匹配:
时间复杂度:O(mn)
import java.util.Scanner;
public class Main {
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
String S = scanner.next();
String T = scanner.next();
int index = Brute(S, T);
System.out.println(index);
scanner.close();
}
private static int Brute(String S, String T) { //S为主串,T为模式串
int i = 0, j = 0;
while (i < S.length() && j < T.length()) {
if (S.charAt(i) == T.charAt(j)) {
i++;
j++;
} else { //回到主串的下一个位置继续匹配
i = i - j + 1;
j = 0;
}
}
if (j == T.length())
return i - T.length();
else
return -1;
}
}
2.KMP
时间复杂度:O(m+n),m、n分别为主串和模式串的长度
- next数组的含义:在模式串的第
j
个字符与主串失配时,则跳到模式串的next[j]
位置重新与主串当前位置进行比较,例如next[1] = 0
,模式串从下标为0的开始与主串下标为i
的位置继续匹配。 - 注意边界,设
next[0] = -1
,KMP匹配的时候不会访问next[0]
- 从原理上理解:next数组是由模式串的最长公共前后缀PM得来的,为了方便,有时候会将得到的PM数组整体右移一位,空下的next[0]置为-1,然后将所有的PM值+1,这种理解方式便于做题的时候快速得到next数组,如果题目中给的下标是从0开始的,那么右移以后就不用+1了。
import java.util.Scanner;
public class Main {
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
String S = scanner.next();
String T = scanner.next();
int index = KMP(S, T, getnext(T));
System.out.println(index);
scanner.close();
}
private static int[] getnext(String T) {
int i = 0, j = -1; //设j为-1,第一次进入循环一定使next[1] = 0;
int[] next = new int[T.length() + 1];
next[0] = -1;
while(i < T.length()){
if(j == -1 || T.charAt(i) == T.charAt(j)){ //小技巧
i++;
j++;
next[i] = j;
}else{
j = next[j];
}
}
/*
for(int a : next) {
System.out.print(a + " ");
}*/
return next;
}
private static int KMP(String S, String T, int[] next) { //S为主串,T为模式串
int i = 0, j = 0;
while (i < S.length() && j < T.length()) {
if (j == 0 || S.charAt(i) == T.charAt(j)) { //第一个字符失配,不需要访问next[0],两个下标均加1即可
i++;
j++;
} else { //主串不回溯,模式串回到下标为next[j]
j = next[j];
}
}
if (j == T.length())
return i - T.length();
else
return -1;
}
}
贴一道关于next数组的题目:1392. 最长快乐前缀