KMP算法易理解

题目:查找B串在A串中的位置。
KMP优点及原因:
A:QWPQW RTY QWPQW PO
B:QWPQW P

B串与A串一个字符一个字符的进行比较,当到达某一位置两字符不等(P、R不等),此时这两字符前面的串都是相等的(QWPQW),此时最好的形式是:直接到达如下的比较阶段。为什么能到达如下的阶段呢?因为某一位置两字符不等,此时这两字符前面的串都是相等的,因此这个位置的比较只由B串决定,那么就可以提前知道应该跳转比较到什么位置。

运用KMP:
A:QWPQW RTY QWPQW PO
: QW PQW P
一般:
A:QWPQW RTY QWPQW PO
: QW PQW P
—————————————————————————
思路:
构造next数组其实就是计算模式串s前缀表的过程。 主要有如下三步:
1:初始化
2:处理前后缀不相同的情况
3:处理前后缀相同的情况
next 数组 下标代表:后缀尾(比较失败字符的前一个字符)
next数组内的值代表:与比较失败的字符进行比较的字符的下标。所以这个值就是最长字符串前缀的长度

next数组下标是后缀尾next【j-1】,j-1为后缀尾。

i 是后缀尾(也是要遍历的数组的下标):要比较字符前一个字符的下标。
j 是前缀尾(j 也用来表示要与比较失败字符进行比较的字符的下标):与P要比较的字符的下标即 R的下标。
若后缀尾与前缀尾相等,则 j++;next【i】 = j。
若后缀尾与前缀尾不相等,则寻找最大前后缀中的最大前后缀,直到找到后一个值刚好等于S【i】(j = next【j-1】),或者不存在满足条件的最大前后缀。(j <= 0跳出循环)。
i,j 代表的都是前后缀尾,i,j是互相对应的,将 j = next【j-1】看成 i = next【i-1】(数组下标即后缀尾)这个next【j-1】是 j 应该比较的元素即这个next【i-1】是 i 应该比较的元素,若相等则满足条件,若不相等经历了一次j = next【j-1】
则继续此时 j 为i应该比较的元素的下标,j-1为。

比较过程如下:
P A Q P W P A Q P  RTY 				PAQPWPAQP AO
P A Q P W P A Q P  j   				PAQPWPAQP i	第一回
P A Q P j1/next[j-1]    	  	    	 PAQP i	第二回
P j2/next[j1-1]				   			    P i	第三回
即寻找最大前后缀中的最大前后缀,直到满足此值相等为止。

next[j-1] 表示 j 位应该匹配的值的位置。

不管第一回还是第二回比较的都是将 j位置的值与 i位置的值进行比较。
next【j-1】:即寻找最大缀。‘
j = next【j-1】:是将缀尾后面一个值赋给 j(缀尾)
的值即与 i位置对应的值

QWPQW  RTY QWPQW PO
		   QWPQW R	//第一回P、R不相等,则j = next【j-1】
			  QW P	//刚刚好匹配	
此时的流程是查找P公共前后缀的前缀的公共前后缀即Q 如下:然后比较Q后面的是不是W,W后面的是不是P如果是则找到(即Q依次往后比较直到P全部相等则成功)
   Q		

—————————————————————————
怎么思考:
脑海中存在两个前后缀的串以及一个值,和 i,j 指针的指向。图如下:
第一步:
@@@@@1----------------@@@@@@4@3-----@2
要找到一个@2的最长前后缀,后缀尾为@3。
第二步:
@@@@@1----------------@@@@@@4----@3-----@2
求next【@3】看最长前缀尾后面一个元素是否与@3相等。
若相等则:j++;next【i】 = j。
若不相等:寻找最大前后缀中的最大前后缀,直到满足此值相等为止。

在这里插入图片描述

非常容易理解 例:
总结就是:
查找P的公共前后缀,同时比较next【P】与P的值,不满足则缩小范围找next【T】的公共前后缀中的公共前后缀,再比较next【W】与W,成功在比较next【W+1】与P。

QWPQW  RTY QWPQW PO
		   QWPQW R	//R与 T不匹配
			  QW P	//刚刚好匹配	
此时的流程是查找P公共前后缀的前缀的公共前后缀即Q 如下:然后比较Q后面的是不是W,W后面的是不是P如果是则找到(即Q依次往后比较直到P全部相等则成功)
   Q		

第二块案例代码:

比较过程如下:
PPQWPPQW  RTY PPQWPPQW AO
		      PPQWPPQW ?	//R与 T不匹配
			      PPQW ?	//刚刚好匹配		 
即寻找最大前后缀中的最大前后缀,直到满足此值相等为止。

自己思考难以理解的原因主要是,当查找next【P】的值与P不一致后,自己知道要查找到一个更小的匹配的串,但是不知道怎么查找,在PPQWPPQW中查找即可。

代码(此时采用前缀表统一减1)

void getNext(int* next, const string& s){
    int j = -1;
    next[0] = j;
    for(int i = 1; i < s.size(); i++) { // 注意i从1开始
        while (j >= 0 && s[i] != s[j + 1]) { // 前后缀不相同了
            j = next[j]; // 向前回退
        }
        if (s[i] == s[j + 1]) { // 找到相同的前后缀
            j++;
        }
        next[i] = j; // 将j(前缀的长度)赋给next[i]
    }
}

一般代码(前缀表不减1):

void getNext(int* next, const string& s) {
        int j = 0;
        next[0] = 0;
        for(int i = 1; i < s.size(); i++) {
            while (j > 0 && s[i] != s[j]) { // j要保证大于0,因为下面有取j-1作为数组下标的操作
                j = next[j - 1]; // 注意这里,是要找前一位的对应的回退位置了
            }
            if (s[i] == s[j]) {
                j++;
            }
            next[i] = j;
        }
    }

按照以下顺序书写就会出错,因为假如s[j] == s[i] 此时 j++了,但是 i 没有加1,下面while中又有比较会造成 j 的改变。

if(s[j] == s[i]) j++;
while (j > 0 && s[i] != s[j]){
               j = next[j-1];
           }          
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值