给定一个目标串,一个模式串。判断目标串是否包含模式串,返回目标串开始匹配的地址。
BF算法
BF算法采用穷举,每次不等时目标串 i加1,模式串j回退到0。平均时间复杂度为O(M*N)
static int bf(String yuan, String target){
char[] yuanChar = yuan.toCharArray();
char[] targetChar = target.toCharArray();
int i = 0, j = 0;
while (i < yuan.length() && j < target.length()){
if (yuanChar[i] == targetChar[j]){
i++;
j++;
}else {
// i-j表示之前已经发生过的不相等情况
i = i-j +1;
j = 0;
}
}
if (j == target.length()){
return i-target.length();
}else {
return -1;
}
}
KMP
KMP算法是BF算法的优化,主要减少了在目标串中下标i的比较次数。模式串下标j还是会每次归零。
例如:
S串 a a a a b
T串 a a a b
第一轮比较从S[0]、T[0]开始,比较到S[3]、T[3]时,值不相等。
按照BF算法,这时要从S[1]、T[0]再次开始比较。
可以看到S串、T串前三位是相等的。
S[0] = T[0] S[1] = T[1] S[2] = T[2]
在T[3] = b 的前面的三个字符是a a a,a a a 的前两位是 a a,后两位也是a a。
根据T[0] = T[1] = T[2]还有上面得到的相等信息,可以得到如下图所示的信息。
按照BF算法,就要拿S[1]和T[0]开始比较。由上图,完全可以从S[3]、T[2]开始下一轮的比较。
KMP算法核心:next数组
KMP使用next[j]来保存在T[j]字符前有多少个字符和T串开头的多少个字符相等,也就是最大前后缀的长度。next数组是KMP算法的核心。
例如 a a a的最大前后缀是 a a
a b a b的最大前后缀是 a b
默认next[0] = -1 ,表示不保存匹配信息。
T串 a a a b的next数组如下:
在有了next[j]数组后,当S[i]和T[j]不匹配时,拿S[i]和T[next[j]]进行比较,这就是KMP算法主体。
先看下KMP算法、求next数组的代码实现:
static int is(String yuan, String target){
int[] next = getNext(target);
char[] yuanChar = yuan.toCharArray();
char[] targetChar = target.toCharArray();
int i = 0, j = 0;
while (i < yuan.length() && j < target.length()){
if (j == -1 || yuanChar[i] == targetChar[j]){
i++;
j++;
}else {
j = next[j];
}
}
if (j == target.length()){
return (i - target.length());
}else {
return -1;
}
}
public static int[] getNext(String target)
{
char[] targetChar = target.toCharArray();
int[] next = new int[targetChar.length];
// 初始条件
int j = 0;
int k = -1;
next[0] = -1;
// 根据已知的前j位推测第j+1位,length需要-1 防止越界
while (j < targetChar.length - 1)
{
if (k == -1 || targetChar[j] == targetChar[k])
{
next[++j] = ++k;
}
else
{
k = next[k];
}
}
return next;
}
给出一个新的模式串并结合代码来看next数组的实现:
a b a c a b a b d
首先 初始化k = -1,k这个变量的意义就是最大前后缀的大小。
next数组代码if分支next[++j] = ++k的意义
求next[3]
进入while循环时,k = 0 ,j = 2。k是next[2]的值,是ab的最大前后缀。
k = 0 表示ab还没有匹配到,当下一个字符T[2] = a过来时,拿T[K]也就是开头还没有匹配到的字符和T[j]判断。如果相等,显然aba就有了最大前后缀 为1。所以next[3] = 1;
if分支k=next[k]的意义
求next[8]
(1)进入while循环时,k = 3, j = 7。
T[3] != T[7] c != b
(2)
在j = 7 时,next[7] = 3; 开头三位是匹配的①等于②
在j = 3 时,next[3] = 1;开头一位是匹配的③等于④
根据以上条件可以得到⑤等于⑥ ,③等于⑥。这时候有一位匹配了,也就是next[k =3]的值,只需要拿③下一个字符去匹配T[7]的b。
经过k=next[k],k 值修改为1并再次回到while循环判断。最后T[1] = T[7],所以next[8] = 2