字符串的比配是一個很重要的點,去年參加校園招聘的時候考了這個,今天翻開以前的教程看到了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;
}
}
}