1.第一种方式是暴利匹配方式
暴利匹配规则
模型: str1 位源字符串下标为i,str2位匹配字符串,下标为j 。 假设 str1 匹配到i , str2 匹配到j 则有
- (1)当 str1[i]==str2[j] 则 i++,j++ 继续匹配下一个字符串
- (2)当 str1[i]!=str2[j] 则 i=i-j+1(即 i向后移动一位),j则被置为0 即:j=0
- 暴利匹配存在的问题,是存在大量的回溯问题,若不匹配则,移动到下一位,接着匹配。
2.第二种方式采用kmp 方式进行匹配
实质是对算法的进一步优化。
- 主要是求出最长公共子序列的长度:规则是求前缀和后缀的共有元素
- 比较过程中相比 暴利匹配,不能直接回归到开始位置+1操作,而是找到公共位置字符串+1操作
- 找到公共部分,需要使用前缀,后缀的公共的部分,来计算出公共子序列。(计算出部分匹配表)
部分匹配表的产生
部分匹配值”就是前缀和后缀的最长的共有元素的长度
- 举例: 以“ABCDABD”为例
注意 !字符串为A 没有前缀和后缀,共有元素(匹配的值)为0;
思路分析:
1.先得到子串的部分匹配表
2.使用部分匹配表完成kmp匹配(kmp 思想 =已经匹配的值-部分匹配值)
3. 相应的代码
package acm;
import java.util.Arrays;
/**
* @author qxl
*/
public class KmpMatch {
public static void main(String[] args) {
String kmp ="abcdabcf";
int[] ints = kmpMatch(kmp);
System.out.println(Arrays.toString(ints));
String str1="ababcddedfabc";
String str2="abcda";
kmpSearch(str1, str2);
}
private static int kmpSearch(String str1, String str2) {
// 获取部分匹配表
int[] next = kmpMatch(str2);
for(int i = 0, j = 0; i< str1.length(); i++){
while (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;
}
}
public static int[] kmpMatch(String source){
// 设置部分部分匹配标记 例如 0,0,0,1,2 表示 前缀后缀的公共部分为2
int [] next=new int[source.length()];
//字符串为1时 前缀和后缀为空集
next[0]=0;
// i=1 表示后缀,j=0 表示前缀
for(int i=1,j=0;i<source.length();i++){
// source.charAt(i) !=source.charAt(j) j需要已匹配的滑动窗口中获取新值next[j-1]
while(j>0 && source.charAt(i)!=source.charAt(j)){
j=next[j-1];
}
if(source.charAt(i)==source.charAt(j)){
j++;
}
next[i]=j;
}
return next;
}
}