字符串的比配是一个很重要的点,去年参加校园招聘的时候考了这个,今天翻开以前的教程看到了KMP,就总结一下吧,其思想还是挺好的。
对于字符串匹配,最简单、最笨、最低效的方法就是循环匹配,也就是:从主串的第一个字符开始,依次与字串进行比较,即把主串的每一个字符当作字串的首字符,直到匹配成功或者遍历完主串(失败)。
可想而知其中包含了很多冗余的操作,因为某次匹配失败便要回滚主串到上次开始的下一个字符,开始下一轮匹配,然而有些回滚是不需要的,如:
1,字串中没有重复字符的时候:
主串:efgrabcdefgadad 用i循环
字串:efga 用j循环
第一轮匹配(i=0):当i循环到3(主串的r位置)时,匹配失败(r和a不相同),便要开始下一轮匹配,即i回滚到1,j回滚到0
观察字串可以发现,e和后面的所有字符都不相同,同样,字串的e和主串的fg也不相同,所以不比较知道不相同,所以第二轮和第三轮比较可以直接跳过,即i不需要回滚
即下一轮比较只需要从i不变,j变为0开始;
2,字串中有重复字符的时候:
主串:abdfgabefaexababdfgabxggadad 用i循环
字串:abdfgabx 用j循环
可以知道,第一轮匹配失败后,因为字串的a和bdfg都不相同,所以都不用比较,而ab和ab相同,不比较也知道,所以也不需要比较,所以下一轮比较只需要把j回滚到d的位置开始匹配即可,I不需要回滚;从上面的分许可以看出,无论字串有没有重复,i都不需要回滚,只需要回滚j,而且重复和不重复的时候j的回滚是不相同的。
以上就是KMP算法的思想,旨在消除不必要的回滚,其最重要的就是next()方法(用于求出j下一次需要回滚到的位置)
例子:
1,main方法:
public static void main(String[] args) {
String sub = "abefr";String str = "dfabfeabefrfef";
int[] next = next(sub);
pattern(str,sub,next);
}
输出:6
2,next()方法:public static int[] next(String sub) {
int[] next = new int[sub.length()];
char[] subs = sub.toCharArray();
int length = sub.length();
int i,j;
next[0] = -1;
i = 0;
for(j = 1;j < length;j++) {
i = next[j - 1];
while(i >= 0 && subs[j] != subs[i+1]) {
i = next[i];
}
if(subs[j] == subs[i+1]) {
next[j] = i+1;
}
else {
next[j] = -1;
}
}
return next;
}
public static void pattern(String str,String sub,int[] next) {
char[] ch1 = str.toCharArray();
char[] ch2 = sub.toCharArray();
int i = 0,j = 0; //i控制ch1,j控制ch2;
for(;i < ch1.length; ) {
if(ch1[i] == ch2[j]) {//匹配就自动递增,往后匹配。
if(j == ch2.length-1) {
System.out.println(i-ch2.length+1);
break;
}
j++;
i++;
}
else if(j == 0) {
i++;
}
else {
j = next[j-1]+1;
}
}
}