KMP笔记

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/zhaohaibo_/article/details/83189759

写在开头:KMP真的好难理解。。

KMP核心思想:利用已经部分匹配这个有效信息,保持i指针不回溯,通过修改j指针,让模式串尽量地移动到有效的位置。

背景:如下图,模式串在j处失配,下一次模式串向后移3个单位(j=3)可重新匹配,直接移3个会比普通的回溯(移1个单位)更有效。

如何确定失配时,下一次应该匹配的位置(模式串后移的单位)?

  • next数组:指导模式串在j位置失配时,下一次应该匹配的模式串的位置为next[j]。上例中,j=6时失配,保持文本串不动(i不动),令模式串动移动到j=3,即next[6] = 3
  • :next[6]=3: 除了可以理解为后移3个单位,也可以理解为: 以i=6结尾,最多与A的前缀匹配到3.

如何确定next[j] ?

  • 在模式串P[1,j)中,最大子匹配的 记模式串的真前缀和真后缀的长度为k,则next[j] = k+1(新的j位置前面k个位置要匹配,第k个位置是要与主串的i位置判断的)。上例中,模式串为ABCABB,j=6时失配,其真前缀为A,AB,ABC,ACBA,ABCAB;其真后缀为B,AB,CAB,BCAB,ABCAB,最大的匹配长度为2,即AB匹配AB,则next[6] = 2+1 = 3。

实战一下求解next数组:

模式串 = ababaaab

对模式串求next数组

num 1 2 3 4 5 6 7 8
P a b a b a a a d
next 0 1 1 2 3 4 2 2

然而,我们不可能通过一个一个数前缀后缀的方式确定next数组的每一个元素。

我们通过下列三个规则计算KMP算法中的next数组:

  1. 初始化next[1] = j = 0,假设next[1 ~ i-1]已求出,下面求解next[i]。
  2. 不断尝试扩展匹配长度j,如果扩展失败(下一个字符不想等),令 j 变为 next[ j ],直至 j 为0(应该重新从头开始匹配)。
  3. 如果能够扩展成功,匹配长度 j 就+1,next[ i ] 的值就是 j 。
// 计算模式串next数组
void calc_next() {
    Next[1] = 0;
    for (int i=2,j=0;i<=lb;i++){
        while(j && b[i]!=b[j+1])
            j=Next[j];
        if(b[j+1]==b[i])j++;
        Next[i]=j;
    }
}
// 寻找所有子串在文本串中出现的起始下标
void find_pattern(){
	for(int i=1, j=0;i<=la;i++){
		while(j>0 && b[j+1]!=a[i])
			j=next[j];
		if (b[j+1]==a[i]) 
			j++;
		if (j==lb){
			cout<<i-lb+1<<endl;
			j=next[j];
		}
	}
}

猜你喜欢

转载自blog.csdn.net/zhaohaibo_/article/details/83189759
kmp
今日推荐