KMP算法解决匹配问题,一个字符串是否包含另一个字符串。
【题目】
两个字符串str和match,长度为N和M,实现一个算法,如果字符串中包含match,则返回match在str中的开始位置,不含有则返回-1.
【举例】
str = “acbc” match = “bc” 返回2
str= “acbc” match = “bcc” 返回-1
【思路】
普通解法:
遍历str,看当前字符是否匹配match
eg:str= “aaaaaaaaaaab” match = “aaaab”
从str[0]开始匹配到str[4],str[4] 与match[4] =‘b’不一样,匹配失败
再从str[1]开始匹配…
直到str[7…11] 与,match匹配,返回11 ,复杂度为O(N*M)
KMP解法:
(1)生成match字符串的nextArr数组**(怎么确定数组的值)**
eg:match = “aaaab”
nextArr[4]应该为多少
match[4] = ’b‘它之前为“aaaa”
这个字符串的后缀子串和前缀子串最大匹配为“aaa”
后缀子串:match[1…3] ==“aaa”
前缀字串:match[0…2] ==“aaa”
所以,nextArr[4] = 3
eg:match = “abc1abc1”
nextArr[7] 为多少
match[7] ==‘1’ 它之前的字符串为“abc1abc”
即后缀子串和前缀子串最大匹配为abc
后缀子串:match[4…6] ==“abc”
前缀字串:match[0…2] == “abc”
这时不仅相等,而且是所有前缀和后缀可能性中最大的匹配
所以nextArr[7] = 3
(2)如何加速进行str和match匹配过程,(有了nextArr之后)
从str[i]开始匹配到str[j]和match[j-i]匹配失败
nextArr[j-i] 表示match[0,j-i-1]这段字符串前缀和后缀的最长匹配
所以下一次检查就可以直接让str[j]与match[k]匹配(而不是str[i+1])
代码实现
public int getIndexOf(String s,String m){
if(s == null || m == null || m.length()<1 || s.length() < m.length()){
return -1;
}
char[] ss = s.toCharArray();
char[] ms = m.toCharArray();
int si = 0;
int mi = 0;
int[] next = getIndexOf(ms);
while (si < ss.length && mi < ms.length){
if(ss[si] == ms[mi]){
si++;
mi++;
}else if(next[mi] == -1){
si++;
}else {
mi = next[mi];
}
}
return mi == ms.length ? si - mi : -1;
}
如何快速得到match字符串的nextArr数组
对于match[0],在它之前没有字符,所以nextArr[0]为-1
对于match[1],在它之前有match[0],所以nextArr[0]为0(nextArr数组的定义要求任何子串的后缀不能包括第一个字符match[0])
对于match[i]:
通过比较可知B字符前的字符串最长前缀与后缀匹配区域,
I区域为最长匹配前缀,K区域为最长匹配后缀,
比较C和B是否相等
如果相等,那么A字符之前的字符串的最长前缀和后缀匹配区域就可以确定,
前缀为I+C,后缀为K+B
即nextArr[i] = nextArr[i-1]+1
如果不相等,看字符C之前的前缀和后缀匹配情况,
m和n分别是C之前的字符串最长匹配的后缀和前缀区域,这时通过nextArr[cn]确定的,两个区域相等,m和m’区域也相等,接下来比较D和B是否相等即可。
如果相等,前缀为n+D,后缀为m’+B,则令nextArr[i] = nextArr[cn]+1
如果不相等,则继续往前跳到D,只要有相等的情况,nextArr[i]的值就能确定。
如果跳到最左位置,即match[0]位置,此时nextArr[0] == -1,说明A之前的字符串不存在前缀和后缀匹配的情况,令nextArr[i] = 0
代码实现
public static int[] getNextArray(char[] ms){
if(ms.length == 1) return new int[] {-1};
int[] next = new int[ms.length];
next[0] = -1;
next[1] = 0;
int pos = 2;
int cn = 0;
while (pos < next.length){
if(ms[pos - 1] == ms[cn]){
next[pos++] = ++cn;
}else if(cn > 0){
cn = next[cn];
}else {
next[pos++] = 0;
}
}
return next;
}
参考:《程序员代码面试指南:IT名企算法与数据结构题目最优解》