暴力匹配法
如果是用暴力匹配的思路,并假设现在str1匹配到i位置,子串str2匹配到j位置,则有:
- 入股当前字符匹配成功(即str1[i] == str2[j]),则i++, j++,继续匹配下一个字符
- 如果失配(即str1[i] != str2[j]),令i= i - (j - 1), j = 0; 相当于每次匹配失败时,i回溯,j被置为0
- 用暴力方法解决的话就会有大量的回溯,每次只移动一位,若是不匹配,移动到下一位接着判断,浪费大量时间
- 暴力匹配算法实现
package KMP;
// 暴力匹配算法实现
public class ViolenceMatch {
public static void main(String[] args) {
String str1 = "jaklsdjaowijdlkaj";
String str2 = "jao";
System.out.println(violenceMatch(str1, str2));
}
// 暴力匹配算法
public static int violenceMatch(String str1, String str2){
char[] s1 = str1.toCharArray();
char[] s2 = str2.toCharArray();
int s1Len = s1.length;
int s2Len = s2.length;
int i = 0, j = 0;
while (i < s1Len && j < s2Len){
if (s1[i] == s2[j]) {
i++;
j++;
} else { // 没有匹配成功
// 如果失败 ,将i回溯向前移动一位,将j重新置为0
i = i - (j - 1);
j = 0;
}
}
if(j == s2Len){
return i-j;
}else {
return -1;
}
}
}
KMP
KMP方法算法就利用之前判断过信息,通过一个next数组,保存模式串中前后最长公共子序列的长度,每次回溯时,通过next数组找到,前面匹配过的位置省去了大量的计算时间
KMP的关键就是查找重复的子串,寻找公共子序列,拿“ABCDABD”来举例子:
首先我们获取A,它的前缀没有,后缀没有那么,自然没有重复公共子序列
之后我们又读取了一个B,与之前的A形成了“AB”字符串,它的前缀有{“A”},后缀有{“B"},很明显前缀和后缀之间没有重复的,所以也是没有重复公共子序列
之后我们读取了一个C,与之前的“AB”形成“ABC”字符串,它的前缀有{“A”,“AB”},后缀有{“C”,“BC”},很明显前缀和后缀都没有重复的,所以没有重复公共子序列
继续往下读,读取到一个D,与之前的“ABC”形成“ABCD”,它的前缀有{“A”,“AB”,“ABC”},后缀有{“D”,“CD”,“BCD”},很明显前缀和后缀也没有重复的,所以没有重复公共子序列
接下来我们继续向下获取,得到一个A,形成了“ABCDA”字符串,它的前缀有{“A”,“AB”,“ABC”,“ABCD”},后缀有{“A”,“DA”,“CDA”,“BCDA”},这时我们看到前缀和后缀之间都有一个“A”,这个A就是它们之间重复公共子序列,我们在当前位置标记一个1
接下来继续读取,得到B,组成“ABCDAB”,前缀是{“A”,“AB”,“ABC”,“ABCD”,“ABCDA”},后缀有{“B”,“AB”,“DAB”,CDAB",“BCDA”},我们发现它的前缀和后缀存在公共子序列,就是“AB”,那么我们将当前位置标记为2(因为“AB”长度是2)
接下来继续读取,得到D,组成“ABCDABD”,前缀是{“A”,“AB”,“ABC”,“ABCD”,“ABCDA”,“ABCDAB”},后缀有{“D”,“BD”,“ABD”,“DABD”,CDABD",“BCDAD”},我们发现它的前缀和后缀不存在公共子序列
就此我们得到了一个表:
package KMP;
import java.util.Arrays;
// KMP
public class KMPAlgorithm {
public static void main(String[] args) {
String str1 = "BBC ABCDAB ABCDABCDABDE" ;
String str2 = "ABCDABD";
int[] next = kmpNext(str2);
System.out.println(Arrays.toString(next));
int index = kmpSearch(str1, str2, next);
System.out.println(index);
}
// 获取到一个字符串(子串)的部分匹配值
public static int[] kmpNext(String dest){
// 创建一个next数组,保存部分匹配值
int[] next = new int[dest.length()];
next[0] = 0; // 如果dest只有一个字符,那部分匹配值就是0
for (int i = 1, j = 0; i < dest.length() ;i++){
while (j > 0 && dest.charAt(i) != dest.charAt(j)){
// 我们需要从next[j-1]获取新的j
// 直到我们发现有满足时才推出
j = next[j - 1];
}
// 当这个条件满足时,部分匹配值就是 +1
if (dest.charAt(i) == dest.charAt(j)){
j++;
}
next[i] = j;
}
return next;
}
// 搜索算法
public static int kmpSearch(String str1, String str2, int[] next){
for (int i = 0, j = 0; i < str1.length(); i++) {
// 需要考虑当两个不相等的时候
if(j > 0 && str1.charAt(i) != str2.charAt(j)){
j = next[j - 1];
}
if(str1.charAt(i) == str2.charAt(j)){
j++;
}
if(j == str2.length()){
return i - j + 1;
}
}
return -1;
}
}