参考来自《拓展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. 【函数式】Monads模式初探——for解析式

    for表达式是monad语法糖 先看一组演示样例: case class Person(name: String, isMale: Boolean, children: Person*) val la ...

  2. Core Java笔记

    前言 一·基础知识 二·定义,关键字和类型 三·表达式和控制流 四·数组 五·对象和类 六·高级语言特性 七·异常 八·图形用户接口 九·AWT(Abstract Window Toolkit) 事件 ...

  3. GoF--装饰者设计模式

    顾名思义,装饰模式就是给一个对象增加一些新的功能,而且是动态的,要求装饰对象和被装饰对象实现同一个接口,装饰对象持有被装饰对象的实例. 装饰器模式的应用场景: 1.需要扩展一个类的功能. 2.动态的为 ...

  4. Web实时通信之Socket.IO

    前面两篇文章使用了Ajax long polling和WebSocket两种常用的Web实时通信方式构建了简单的聊天程序. 但是,由于浏览器的兼容问题,不是所有的环境都可以使用WebSocket这种比 ...

  5. pip更换下载源(提升下载速度)

    经常在使用Python的时候需要安装各种模块,而pip是很强大的模块安装工具,但是由于国外官方pypi经常被墙,导致不可用,或者下载速度很慢,所以我们最好是将自己使用的pip源更换一下,这样就能解决被 ...

  6. [Command] lrzsz - 文件传输工具包

    lrzsz 是一个支持 XMODEM.YMODEM.ZMODEM 文件传输协议的 Unix 程序包.它是 Omen Technologies 公司所有的 rzsz 程序包的公开发行增强版,遵守 GNU ...

  7. tablayout在中间显示

    <android.support.design.widget.TabLayout android:id="@+id/tabLayout" android:layout_wid ...

  8. javaCompileOptions { annotationProcessorOptions { includeCompileClasspath = true } }

    一: 在app的build中android {    ...    defaultConfig {        ...        //添加如下配置就OK了        javaCompileO ...

  9. React Native 开发工具篇

    正文 概述:开发RN的工具有很多,选择性也比较多,比如Facebook专门为React开发的IDE:Nuclide,还有做前端比较熟悉的WebStorm.Sublime Text 3.VS Code等 ...

  10. print($arr,true)的参数true表示将$arr的值返会,而不是打印

    之前通过error_log()来调试php, 发现收到的json字符前边总是有一个1,一直以为是哪里直接出现了print.print_r.echo.var_dump(), 后来发现原来是某处用了形如e ...