场景
求一个字符串(模式串)在另一个字符串(主串)中的位置,称为字符串模式匹配。
KMP算法实现
该算法相对于 Brute-Force(暴力)算法有比较大的改进,主要是消除了主串指针的回溯,从而使算法效率有了某种程度的提高。
假设主串为txt,模式串为p,遍历比较时,主串下标为index1,模式串下标为index2。
主串为:ABCDABCDABE,模式串为:ABCDABE。
KMP流程说明:
- 比如当与主串比较时,发现到模式串的第7个字符E与主串的第7个字符D不匹配,此时index1 = 6,index2 = 6。
- 如果是暴力法,就需要index1 = 2,index2 = 0,重新开始匹配。
- 但是这样很没有效率,我们知道模式串中是有重复字符的:“AB”。
- 我们可以用KMP算法,主串的下标index1 = 6不变,模式串可以从第3个字符C继续开始比较,即index2 = 2。
代码实现的思路:
- 求出模式串p的next数组,next数组中存着每个下标回溯的位置。对于上面的例子来说,就是next[6] = 2.
- 当模式串p中的字符与主串中的字符不匹配时,主串txt的下标index1不变,模式串p的下标index2变为next[index2].
最难的部分就是求这个next数组。
我们的思路是这样:通过一个值,k,来记录字符前面有多少个字符是重复的。
代码如下:
import java.util.Scanner;
public class KMP {
public static void main(String[] args) {
Scanner in = new Scanner(System.in);
String txt = in.nextLine();
String p = in.nextLine();
System.out.println(new KMP().search(txt, p));
}
public int search(String txt, String p){
int[] next = new int[p.length()];
this.setNext(p, next);
//index1表示txt的下标,index2表示p的下标
int index1 = 0, index2 = 0;
while(index1 < txt.length() && index2 < p.length()){
//如果index2等于-1,说明上次循环发现是第一个就不匹配,所以++
//如果相等,就继续++
//如果不等,index2回溯,index1不用动
if(index2 == -1){
index1++;
index2++;
}else if(txt.charAt(index1) == p.charAt(index2)){
index1++;
index2++;
}else{
//System.out.println("主循环之前" + index2);
index2 = next[index2];
//System.out.println("主循环之后" + index2);
}
}
if(index2 >= p.length()){
//System.out.println(index1 + " " + index2);
return index1 - index2;
}else{
return -1;
}
}
/**
* 获取模板串的next数组
* 条件:对于字符串p(“ABCDABD”),当前字符为w(最后一个字符'D')
* 思路:w前面有两个字符(“AB”)与p最开头的字符相同,那么如果w不匹配,p的下标应该变为2,从p的第三个字符('C')开始比较
* @param p
* @param next
*/
public void setNext(String p, int[] next){
//next数组的0/1是固定的
next[0] = -1;
next[1] = 0;
//k记录当前字符前面相同字符的个数,-1为初始值
//比如字符串为ABCDABD,对于第6个字符B来说,k为1,对于最后一个字符D来说,k为2,前面AB相同
int k = -1;
//循环赋值,
for(int i = 2; i < p.length(); i++){
//对于k = -1的情况,说明前一个字符是不相等,应该从开头开始,k=0;
//对于前一个字符与第k个字符相等的情况,应该对k进行++
//对于k 不等于 -1,并且字符不相等的情况,k就需要变为0,重新开始,但是呢,不用重复从0开始,直接回溯到k就行了
if(k == -1){
k++;
next[i] = 0;
}else if(p.charAt(i - 1) == p.charAt(k)){
k++;
next[i] = k;
System.out.println("这是" + i + "的" + k);
}else{
k = next[k];
i--;
}
}
}
}
参考:
https://www.cnblogs.com/imzhr/p/9613963.html