字符串经典算法KMP

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;
    例如:str=abcdabcdf。next[7]=len(“abc”)=3,而str[3]=str[7]=‘d’,所以next[8]=4。
  • 如果next[i]=k,str[k]!=str[i],那么就在以k结尾的前缀子串中寻找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
字符串经典算法KMP_第1张图片
当i->g,j->f不匹配时,由于next[f]=len(abc)=3
所以j=next[j],匹配过程变成下面的样子:(相当于下面的子串向右平移)
字符串经典算法KMP_第2张图片

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算法的应用

  1. 两棵二叉树树,判断一棵树是否是另一棵树的子树。
    解法:将二叉树序列化(前序遍历)成唯一字符串,利用KMP算法判断字符串是否是子串。

  2. 一个字符串,在这个字符串末尾最少加上多少字符,可以使得新的字符串包含两个原始字符串。
    例如:abdgab,在字符串后面加上dgab,变成abdgabdgab,包含两个abdgab。
    解法:求next数组,利用next数组的含义求解。

你可能感兴趣的:(算法)