字符匹配算法---KMP算法(图、文、伪代码解释)

字符匹配算法—KMP算法

前言

为什么要讲KMP算法,而不是BM、BF、RK这些算法呢,不是我针对谁在座的都是辣鸡。(PS:仅仅是为了给自己更深刻的印象才写哈哈哈)

KMP算法的思路

目标串(S)与模式串(T)

暴力破解法的话是每次目标串(S)和模式串(T)逐位匹配若相等往后移一位。若不相等模式串(T)从0开始,目标串(S)往后移动一位。
而KMP算法就是利用在暴力破解法匹配过程中前N个字符串已经匹配成功了的信息,让不相等情况下不是往后移一位而是往后移D[j-1]位。(PS:这个D[j-1]在后面会说到,这里只是讲解一下思路

D[j]数组

ps:在多数教材里面用的都是*next[j]*数组,但这里用的是D[j]数组。两个是有一定的不同的,大家可以看完D[j]数组的解释后自行比较一下。在文章末尾会做一个对比。
在这里插入图片描述
D数组定义
D[j]数组描述的是D[0,j]字符串中最长公共前后缀的长度。其中公共前后缀的定义是:前缀必须包含第一个字符串即T[0],后缀必须包含第j个字符串即T[j],然后前缀和后缀的字符串完全一致,例如图中的①和②就是公共前后缀的。最长顾名思义就是这种类型的字符串最长的长度例如②,所以D[3] = 3。
D数组的意义

在这里插入图片描述
字符串匹配原理就是从模式串第0位开始一直往后匹配,当匹配到不相等时候移动N个位置(暴力算法和KMP不同)。假设目前情况如上图中,两个字符串从0开始匹配,当S串匹配到i = 4, T串匹配到j = 4时候发现两者不匹配了。这种情况下证明前4个字符S[0,4]和T[0,4]是完全相等的。其中D数组描述的是T字符串中第N位的最长公共前后缀,就是D[3] = 3(数学基本理论要来咯,不懂得面壁)。证明③和④是相同的,那么①和②也是相等的,所以最后结论是②和③是相等,那么在j = 4时候匹配失败了,我们只要从T数组下标为3开始重新进行匹配即可。那么这个3又是从哪来呢。就是我们的D[j - 1] = D[3] = 3。

KMP算法伪代码

while(i < S.length){
	if(S[i] == T[j]){//如果相同,两个下标同时往后延
		i++;
		j++;
		if(j == T.length){
			//输出匹配到的字符串下标为:j - i
			break;
		}	
	}else{//如果不相等,移动相应位置
		if(j == 0){//如果模式串(T)下标为0,证明一开始都没有匹配成功
			i++; //目标串(S)下标往后移动一位,模式串(T)不变
		}else{//如果模式串(T)下标不是0
			j = D[j - 1]; // j从D数组的j-1开始匹配,i不变
		}
	}
}

计算D数组的思路
其实就是一个简单的动态规划,j=0时候,定义D[0] = 0,然后每次重新计算第j位最长公共前后缀时候,因为D[j - 1]描述的是前j位的最长公共前后缀,现在只要将第j位的字符T[j] 和第D[j - 1]的字符T[ D[j - 1] ]进行比较,若相同就是最长公共前后缀加1,若不相同就是0。
D数组伪代码

public static int[] getDarray(String pStr){
        int[] D = new int[pStr.length()];

        for(int i = 0 ; i < pStr.length(); i++){
            if(i == 0){
                D[i] = 0;//D数组的第一个设置为0
            }else{
                int j = D[i - 1];
                if(pStr.charAt(j) == pStr.charAt(i)){
                    D[i] = D[i - 1] + 1;
                }else{
                    D[i] = 0;
                }
            }
        }

        return D;
    }

这是个人的见解与想法若有哪里出错,或者不懂可留言批判或询问。

猜你喜欢

转载自blog.csdn.net/weixin_43118891/article/details/107903069