参考来自《拓展kmp算法总结》:http://blog.csdn.net/dyx404514/article/details/41831947

扩展KMP解决的问题:

定义母串S和子串T,S的长度为n,T的长度为m;

求  字符串T  与  字符串S的每一个后缀  的最长公共前缀;

也就是说,设有extend数组:extend[i]表示T与S[i,n-1]的最长公共前缀,要求出所有extend[i](0<=i<n)。

(注意到,如果存在若干个extend[i]=m,则表示T在S中完全出现,且是在位置i出现,这就是标准的KMP问题,所以一般将它称为扩展KMP算法。)

下面举一个例子,S=”aaaabaa”,T=”aaaaa”;

首先,计算extend[0]时,需要进行5次匹配,直到发生失配:从而得知extend[0]=4;

下面计算extend[1],在计算extend[1]时,是否还需要像计算extend[0]时从头开始匹配呢?

答案是否定的,因为通过计算extend[0]=4,从而可以得出S[0,3]=T[0,3],进一步可以得到 S[1,3]=T[1,3];

计算extend[1]时,事实上是从S[1]开始匹配;

设有辅助数组next[i]表示T[i,m-1]和T的最长公共前缀长度,

在这个例子中,next[1]=4,即T[0,3] = T[1,4],进一步得到T[1,3]=T[0,2],所以S[1,3]=T[0,2],所以在计算extend[1]时,通过extend[0]的计算,已经知道S[1,3]=T[0,2];

所以前面3个字符已经不需要匹配,直接匹配S[4]和T[3]即可,这时一次就发生失配,所以extend[1]=3.

1. 拓展kmp算法一般步骤

通过上面的例子,事实上已经体现了拓展kmp算法的思想,下面来描述拓展kmp算法的一般步骤。

首先我们从左到右依次计算extend数组,在某一时刻,设extend[0...k]已经计算完毕,并且之前匹配过程中所达到的最远位置为P,所谓最远位置,严格来说就是i+extend[i]-1的最大值(0<=i<=k);

设取到这个最大值的位置为Po,如在上面的例子中,计算extend[1]时,P=3,Po=0.

现在要计算extend[k+1],根据extend数组的定义,可以推断出S[Po,P]=T[0,P-Po],从而得到 S[k+1,P]=T[k-Po+1,P-Po],令len=next[k-Po+1](next[i]表示T[i,m-1]和T的最长公共前缀长度);

分以下两种情况讨论:

第一种情况:k+len<P

如下图所示:

上图中,S[k+1,k+len]=T[0,len-1],

然后S[k+len+1]一定不等于T[len],因为如果它们相等,则有S[k+1,k+len+1]=T[k+po+1,k+Po+len+1]=T[0,len],那么next[k+Po+1]=len+1,这和next数组的定义不符;

所以在这种情况下,不用进行任何匹配,就知道extend[k+1] = len.

一个例子的模拟:

  

第二种情况: k+len>=P

如下图:

上图中,S[P+1]之后的字符都是未知的,也就是还未进行过匹配的字符串,所以在这种情况下,就要从S[P+1]和T[P-k+1]开始一一匹配,直到发生失配为止,当匹配完成后,如果得到的extend[k+1]+(k+1)大于P则要更新未知P和Po.

另一个例子的模拟:

至此,拓展kmp算法的过程已经描述完成,细心地读者可能会发现,next数组是如何计算还没有进行说明;

事实上,计算next数组的过程和计算extend[i]的过程完全一样,将它看成是以T为母串,T为子串的特殊的拓展kmp算法匹配就可以了;

计算过程中的所需要的next数组值全是已经计算过的,所以按照上述介绍的算法计算next数组不会出现问题,不再赘述。

代码模板:

const int MAX=; //字符串长度最大值
int Next[MAX],extend[MAX]; //预处理计算Next数组
void getNext(char str[])
{
int i=,j,po,len=strlen(str);
next[]=len; //初始化next[0]
while(str[i]==str[i+] && i+<len) i++; next[]=i; //计算next[1]
po=; //初始化po的位置
for(i=;i<len;i++)
{
if(next[i-po]+i < next[po]+po) //第一种情况,可以直接得到next[i]的值
next[i]=next[i-po];
else //第二种情况,要继续匹配才能得到next[i]的值
{
j = next[po]+po-i;
if(j<) j=; //如果i>po+next[po],则要从头开始匹配
while(i+j<len && str[j]==str[j+i]) j++; next[i]=j;
po=i; //更新po的位置
}
}
} //计算extend数组
void EXKMP(char s1[],char s2[])
{
int i=,j,po,len=strlen(s1),l2=strlen(s2);
getNext(s2); //计算子串的next数组
while(s1[i]==s2[i] && i<l2 && i<len) i++; extend[]=i;
po=; //初始化po的位置
for(i=;i<len;i++)
{
if(next[i-po]+i < extend[po]+po) //第一种情况,直接可以得到extend[i]的值
ex[i]=next[i-po];
else //第二种情况,要继续匹配才能得到extend[i]的值
{
j = extend[po]+po-i;
if(j<) j=; //如果i>extend[po]+po则要从头开始匹配
while(i+j<len && j<l2 && s1[j+i]==s2[j]) j++; extend[i]=j;
po=i; //更新po的位置
}
}
}

扩展KMP算法小记的更多相关文章

  1. 扩展KMP算法

    一 问题定义 给定母串S和子串T,定义n为母串S的长度,m为子串T的长度,suffix[i]为第i个字符开始的母串S的后缀子串,extend[i]为suffix[i]与字串T的最长公共前缀长度.求出所 ...

  2. 神奇的字符串匹配:扩展KMP算法

    引言 一个算是冷门的算法(在竞赛上),不过其算法思想值得深究. 前置知识 kmp的算法思想,具体可以参考 → Click here trie树(字典树). 正文 问题定义:给定两个字符串 S 和 T( ...

  3. 扩展KMP——算法总结,来自于 迷路的鸽子

    扩展kmp                 LRH 所谓扩展kmp指的是与kmp相似的求辅助数组的原理,但是本身与kmp关系不大. 1.exkmp的用途:给定一个主串s和一个子串t,求出s中每一个后缀 ...

  4. KMP算法小记

    Knuth-Morris-Pratt算法: 转载来自http://www.ruanyifeng.com/blog/2013/05/Knuth%E2%80%93Morris%E2%80%93Pratt_ ...

  5. (模板)扩展kmp算法(luoguP5410)

    题目链接:https://www.luogu.org/problem/P5410 题意:有两个字符串a,b,要求输出b与a的每一个后缀的最长公共前缀.输出: 第一行有lenb个数,为b的next数组( ...

  6. 【2018.07.27】(字符串/找相同)学习KMP算法小记

    虽然说原理很好理解,但是代码理解了花费我一个下午的时间,脑阔痛 该注释的地方都标记了,希望以后看到这些代码我还能好好理解吧 学习的链接地址:https://www.cnblogs.com/teble/ ...

  7. 浅谈Manacher算法与扩展KMP之间的联系

    首先,在谈到Manacher算法之前,我们先来看一个小问题:给定一个字符串S,求该字符串的最长回文子串的长度.对于该问题的求解.网上解法颇多.时间复杂度也不尽同样,这里列述几种常见的解法. 解法一   ...

  8. KMP算法模板&&扩展

    很不错的学习链接:https://blog.csdn.net/v_july_v/article/details/7041827 具体思路就看上面的链接就行了,这里只放几个常用的模板 问题描述: 给出字 ...

  9. HDU3613 Best Reward —— Manacher算法 / 扩展KMP + 枚举

    题目链接:https://vjudge.net/problem/HDU-3613 Best Reward Time Limit: 2000/1000 MS (Java/Others)    Memor ...

随机推荐

  1. [Arch] 02. Design principle and Software Pattern

    Ref: 软件设计的七大原则 有时间的话,还需进一步深入理解. Figure, 重要的前五个原则 单一职责原则 (Simple responsibility pinciple SRP) 类的设计趋向于 ...

  2. @ControllerAdvice -- 处理异常示例

    Class : SessionInterceptor package com.estate.web.filter; import javax.annotation.Resource; import j ...

  3. Go之简单并发

    func Calculate(id int) { fmt.Println(id) } 使用go来实现并发 func main() { for i := 0; i < 100; i++ { go ...

  4. c++的字节对齐

    win32平台下的微软C编译器对齐策略: 1)结构体变量的首地址能够被其最宽数据类型成员的大小整除.编译器在为结构体变量开辟空间时,首先找到结构体中最宽的数据类型,然后寻找内存地址能被该数据类型大小整 ...

  5. javascript实现的网页打印

    打印全部页面: <body onload="javascript:window.print()"> 实现局部打印,可以将不参加打印的元素设置“display=none” ...

  6. 《C++ Primer Plus》16.2 智能指针模板类

    智能指针是行为类似于指针的类对象,单这种对象还有其他功能.本节介绍三个可帮助管理动态内存分配的智能指针类.先来看看需要哪些功能以及这些功能是如何实现的.请看下面的函数:void remodel(std ...

  7. 使用API函数EnumWindows()枚举顶层窗口

      http://simpleease.blog.163.com/blog/static/1596085820052770290/ 要枚举Windows当前所有打开的顶层窗口,可使用Windows A ...

  8. jQuery队列(三)

    看了一下队列剩下的几个方法,在没有应用场景的情况下,对它所做的一些处理不能明白.后续希望可以通过动画部分代码的阅读能搞清楚这些处理的意义.jQuery.fn.extend({ // 推迟队列中函数的执 ...

  9. jQuery的回调管理机制(三)

    jQuery.when()方法是jQuery内部使用回调机制的范例. // 参数为多个方法,这些方法全部执行完成之后执行回调 when: function( subordinate /* , ..., ...

  10. wmsys.wm_concat结果长度限制的问题

    转:http://bbs.csdn.net/topics/360059765 使用wmsys.wm_concat多列合成一列遇到问题ORA-22813: 操作数值超出系统的限制 官方文档解释是总长度超 ...