这里写目录标题
1.BP(暴力匹配)
假设我们现在有两个字符串:
//文本串
String s = "BBC ABCDAB ABCDABCDABDE";
//待匹配字符串
String p = "ABCDABD";
整个算法的分析过程:
首先我们需要两个指针用于扫描两个字符串:
//扫描字符串s
int i = 0 ;
//扫描字符串p
int j = 0 ;
流程如下:
此时就要进行回退操作:
i = i - j ;
j = 0 ;
代码如下:
public static int BP(String s , String p){
//i----扫描s字符串的指针,j---扫描P字符串的指针
int j = 0 ;
for (int i = 0 ; i < s.length() ; i++){
if (p.charAt(j) == s.charAt(i)){
if (j == p.length()-1){
return i - (p.length()-1);
}
j++;
}else {
/*
回退操作:
j回退到第一次匹配成功后的下标
i回退到待匹配字符的首字符的下标(0)
*/
i = i - j ;
j = 0 ;
}
}
return -1 ;
}
2.KMP算法
首先我们来思考一下BP的缺点在什么地方:
这里我直接指出来,在回退操作中,BP算法直接屏蔽掉了 在当前匹配失败之前匹配成功的字符。
这使得我们又要重复扫描一些不需要再次扫描的字符。
那么KMP算法怎么回退呢?
(以下图片资源非本人制作)
我们令 i 不变
而 j = next[ j ] ;
这样做我们就吧重复的AB当作已经匹配好的前缀,再继续扫描字符s
现在我们还不知道next数组是什么,接下里你就会明白了
接下来我们去研究以下next[ ]数组先,一旦next[ ]我们构建完毕,那么KMP算法写起来就十分简单了。
next[ ]数组的构建和具体含义
next[] 数组的含义 : 前缀与后缀最长的公共字符串的长度
例如:
实现代码如下:
private static int[] getNext(String p){
int[] next = new int[p.length()];
/*
这里i(慢) j(快)相当于一个快慢指针
*/
int i = -1, j = 0;
next[0] = i ;
while (j < (p.length()-1)){
if (i == -1 || p.charAt(j) == p.charAt(i)){
i++;
j++;
next[j] = i;
}else {
i = next[i];
}
}
System.out.println(Arrays.toString(next));
return next;
}
KMP算法—完整代码如下:
public static int KMP(String s, String p){
int[] next = getNext(p);
int i = 0 , j = 0 ;
while (i < p.length() && j < s.length()){
if (i == -1 || s.charAt(j) == p.charAt(i)){
i++;
j++;
}else {
i = next[i];
}
}
if (i == p.length())return j - p.length();
return -1 ;
}
private static int[] getNext(String p){
int[] next = new int[p.length()];
/*
这里i(慢) j(快)相当于一个快慢指针
*/
int i = -1, j = 0;
next[0] = i ;
while (j < (p.length()-1)){
if (i == -1 || p.charAt(j) == p.charAt(i)){
i++;
j++;
next[j] = i;
}else {
i = next[i];
}
}
System.out.println(Arrays.toString(next));
return next;
}
KMP算法中i j 的变化如下,会更好的帮助你去理解整个过程。
妙不可言妙不可言