KMP算法
解决寻找子串的问题。
public static int indexOf(String str1,String str2){
...
}
步骤一:求next数组。
next数组的定义:
next[i]表示字符串下标i前面的子串中,最长公共前缀与后缀的长度。规定next[0]=-1,next[1]=0.
例如:str=“abcdabcf”,f的下标为7,next[7]表示子串"abcdabc"的最长公共前缀与后缀"abc"的长度3,next[7]=3.
next数组的求法:
数学归纳法:
- 如果next[i]=k,str[k]=str[i],那么next[i+1]=k+1=next[i]+1;
a | b | c | d | a | b | c | d | f |
---|---|---|---|---|---|---|---|---|
0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 |
例如:str=abcdabcdf。next[7]=len("abc")=3
,而str[3]=str[7]='d'
,所以有next[8]=next[7]+1=4,if(str[7]=str[next[7]])
。
- 如果next[i]=k,str[k]!=str[i],那么该如何求next[i+1]呢?
a | b | c | d | a | b | c | f | g |
---|---|---|---|---|---|---|---|---|
0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 |
例如:str=abcdabcfg. next[7]=len('abc')=3
,str[7]≠str[3]
,该如何求next[8]。
就在以k结尾的前缀子串"abc"中寻找next,k=next[k],重复上面的比较步骤;
- 如果k=-1,那么说明next[i+1]=k+1=0;(编程时,可以与第一种情况合并)
例如:str=abcdabcef。next[7]=len(“abc”)=3,而str[3]!=str[7],所以在前缀子串’abc’中继续寻找next[k]=m,比较是否有str[m]=str[i],如果相等,next[i+1]=m+1;如果不相等,一直寻找下去,直到不存在更小的前缀子串,next[i+1]=0;
public static int[] getNextArray(String str){
if(str == null || str.length() ==0)
return null;
if(str.length() == 1) {
return new int[]{-1};
}
char[] array = str.toCharArray();
int[] next = new int[array.length];
next[0] = -1;
int i = 1;
int j = 0;
while(i < array.length-1) {
// 这里包含了两种情况,第一种是j=-1,,表示找不到公共前后缀了,next[i+1]=0
// 第二种情况是,next[i]=k,str[k]=str[i],那么next[i+1]=k+1;
if(j==-1 || array[i] == array[j]) {
next[++i] = ++j;
}
else if(j >= 0) {
j = next[j];
}
}
return next;
}
步骤二:字符串匹配
KMP算法的本质是利用next数组进行匹配加速。
例如:str1=xxxabcdabcgxxx,str2=abcdabcf
当i->g,j->f不匹配时,由于next[f]=len(abc)=3
所以j=next[j],匹配过程变成下面的样子:(相当于下面的子串向右平移)
public static int indexOf(String str1,String str2){
int[] next = getNextArray(str2);
int i=0;
int j=0;
char[] array1 = str1.toCharArray();
char[] array2 = str2.toCharArray();
while(i < array1.length && j < array2.length) {
// j = -1,说明next[j]之前没有匹配上,next[0]=-1,这是i++,j=0;
if(j == -1) {
i++;
j++;
}
// 匹配上了,i和j同时往后移
else if(array1[i] == array2[j]) {
i++;
j++;
} else {
// 没有匹配上,将子串往右推
j = next[j];
}
}
if(j == array2.length)
return i - j;
else return -1;
}
KMP算法的应用
-
两棵二叉树树,判断一棵树是否是另一棵树的子树。
解法:将二叉树序列化(前序遍历)成唯一字符串,利用KMP算法判断字符串是否是子串。 -
一个字符串,在这个字符串末尾最少加上多少字符,可以使得新的字符串包含两个原始字符串。
例如:abdgab,在字符串后面加上dgab,变成abdgabdgab,包含两个abdgab。
解法:求next数组,利用next数组的含义求解。