题目: 字符串str1和str2 ,str1是否包含str2,如果包含str2在str开始的位置,如何做到时间复杂度O(N)完成?
解法: 用KMP 算法 求解,需要用到一个最长前缀值数组。
字符串中每个字符的最长前缀值是:当前字符前面的字符,前缀串 = 后缀串 ,最长的长度。
有了这个字符的前缀值数组,当在str1,和 str2匹配时候,str1[i] != str2[j],i 不需要回到 i-j+1 的位置了,i仍然不变,只是需要改变j,j = 最长前缀值即可。
这个最长前缀值,维护的是一个数组,即保存每个字符的最长前缀值:next[]。
生成next[]数组的java代码如下:
private static int[] getNextArray(char[] str2) {
if (str2 == null || str2.length == 0){
return null;
}
//1,创建next数组
int[] next = new int[str2.length];
//字符串前两个字符的最长前缀值,都可以直接确定
next[0] = -1;
next[1] = 0;
//从第三字符开始计算
int i = 2;
//cn表示当计算到字符i的时候,需要用到i-1字符已经计算出来的最长前缀值 cn = next[i-1]
// 如果字符i-1的最长前缀值,索引上面的那个字符 str[next[i-1]] 等于 i-1,
// str[cn] == str[i-1] ?
// 那么i字符的最长前缀值就等于next[i-1]+1
// 我们把 cn 设置为 next[i-1],cn 从0开始
int cn = 0;
while (i < next.length){
if (str2[cn] == str2[i-1]){
//如果字符i的前一个字符i-1,的最长前缀匹配值next[i-1],
// 这个位置上的字符str[next[i-1]] = str[i-1]
//那么i字符的最长前缀值,等于i字符的最长前缀值+1, next[i] = cn+1;
// 因为 i要继续向后移动,i要++,
// i+1 字符,还有利用i字符计算出来的最长前缀值了,i利用的是i-1的cn,
// 那么 i+1 需要利用i的 cn+1 了
next[i] = cn+1;// next[i] = next[i-1]+1 也行
i++;//向后移动
cn++;//向后移动
} else if (cn > 0){
cn = next[cn];//如果 str2[cn] != str2[i-1], 那么cn需要向前移动了,移动多少呢?
//需要借助字符 str[cn] 的最长前缀值了,就是 next[cn],
// 有点类似于
// 此时 cn 就是字符i-1, i
} else {
next[i++] = 0;//如果一直找不到,则当前字符i的最长前缀匹配值就是0了,然后i向后移动 i++
}
}
return next;
}
有了这个next[] 数组,kmp 算法就好解了。
应用KMP代码的JAVA代码如下:
public static int kmp(String s, String m){
if (s == null || m == null || m.length() < 1 || s.length() < m.length()){
return -1;
}
//1,字符串转换为字符数组
char[] str1 = s.toCharArray();
char[] str2 = m.toCharArray();
int i1 = 0;
int i2 = 0;
int[] next = getNextArray(str2);// 前缀数组
//2,开始匹配
while (i1 < str1.length && i2 < str2.length){
if (str1[i1] == str2[i2]){//如果字符匹配,则同时向后移动
i1++;
i2++;
} else if (i2 == 0){//当str1[i1] != str2[i2],并且i2==0,说明完全匹配不上,则i1需要向后移动了
i1++;
} else {
i2 = next[i2];//此时str1[i1] != str2[i2],为了节省匹配时间,不应该从str2的头部,即i2==0
//和此前str1的下一个位置比较,
//因为计算了str2每个字符的最长前缀,此时i2位置的字符最长前缀就是 next[i2],从最长前缀位置开始再次比较
}
}
return i2 == str2.length ? i1 - i2 : -1;
}
调试:
public static void main(String[] args) {
System.out.println(kmp("abcabstabcabe", "abcabe"));
}
结果:
本文转载自 微信公众号,皮皮克克—可以叫我Tony 链接
https://mp.weixin.qq.com/s?__biz=MzkxOTM5NzUxMw==&mid=2247485547&idx=1&sn=7eea7cb81611bdfa19e8c49943717487&chksm=c1a3fd8df6d4749b9fc6b901cb0bd8f3159b0da233ccb2aeea8b626eefa671e064dc2a9d21ed&scene=126&sessionid=1685787950#rd