参考来自《拓展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. Java实现快速批量移动文件

    文件移动是计算机资源管理常用的一个操作,这在操作系统中可以通过文件的剪切与复制或鼠标拖动来实现.但是在Java文件的编程实现中,大多是通过复制文件到目的地,再删除所有文件来实现的.这对于小文件来说看不 ...

  2. SpringMVC由浅入深day01_2springmvc入门程序

    2 入门程序 2.1 需求 以案例作为驱动. springmvc和mybaits使用一个案例(商品订单管理). 功能需求:商品列表查询 2.2 环境准备 数据库环境:mysql5.5 先导入sql_t ...

  3. linux 停止对某个端口的监听

    1.通过"netstat -anp" 来查看哪些端口被打开. 2.关掉对应的应用程序,则端口就自然关闭了,如:"kill -9 PID" (PID:进程号)

  4. AESDK开发之UI消息响应

    UI创建: 在该入口下 case PF_Cmd_PARAMS_SETUP: //.... break; 必须在末尾指定UI数目,UI数目一般是枚举,如果和枚举长度不一致也会报错.所以最好是直接修改枚举 ...

  5. linux下getsockopt和setsockopt详解及测试

    linux下getsockopt和setsockopt详解及测试 NAME 名字 getsockopt, setsockopt - get and set options on sockets 获取或 ...

  6. mysql5.6.34-debug Source distribution在树莓派下编译的几个错误

    raspberrypi下编译mysql5.6 debug版源码. 1. 启动错误 和mysqld相关的文件及文件夹权限必须设置为mysql用户可读可写可执行,特别是/var/run/mysqld/目录 ...

  7. 《shiro框架》

    20170929 shiro授权流程学习 shiro-filter执行流程 CacheManager(shiro缓存管理) JEESITE登录流程简单梳理 shiro与springMVC整合 shir ...

  8. 【Java知识点专项练习】之 Java鲁棒性的特点

    Java鲁棒性的特点如下: Java在编译和运行程序时都要对可能出现的问题进行检查,以防止错误的产生. Java编译器可以查出许多其他语言运行时才能发现的错误. Java不支持指针操作,大大减少了错误 ...

  9. 《转》Python学习(17)-python函数基础部分

    http://www.cnblogs.com/BeginMan/p/3171977.html 一.什么是函数.方法.过程 推荐阅读:http://www.cnblogs.com/snandy/arch ...

  10. 六、K3 WISE 开发插件《直接SQL报表开发新手指导 - BOM成本报表》

    ======================== 目录: 1.直接SQL报表 ======================== 1.直接SQL报表 以BOM成本报表为例,在销售模块部署,需要购买[金蝶 ...