原文转自:http://www.cppblog.com/MatoNo1/archive/2011/04/17/144390.aspx

KMP:给出两个字符串A(称为模板串)和B(称为子串),长度分别为lenA和lenB,要求在线性时间内,对于每个A[i] (0<=i<lenA),求出A[i]往前和B的前缀匹配的最大匹配长度,记为ex[i](或者说,ex[i]为满足A[i- z+1..i]==B[0..z-1]的最大的z值)。KMP的主要目的是求B是不是A的子串,以及若是,B在A中所有出现的位置(当 ex[i]=lenB时)。
【算法】
设next[i]为满足B[i-z+1..i]==B[0..z-1]的最大的z值(也就是B的自身匹配)。设目前next[0..lenB-1]与ex[0..i-1]均已求出,要用它们来求ex[i]的值。

据ex的定义,有A[i-1-ex[i-1]+1..i-1]==B[0..ex[i-1]-1],这时,若有A[i]==B[ex[i-1]],则可以
直接得到ex[i]=ex[i-1]+1(因为i-1-ex[i-1]+1即i-ex[i-1],现在由于A[i]==B[ex[i-1]],可得
A[i-ex[i-1]..i]==B[0..ex[i-1]],即A[i-ex[i-1]+1-1..i]==B[0..ex[i-1]+1-1],所
以ex[i]=ex[i-1]+1)。若A[i]!=B[ex[i-1]]?
设j=next[ex[i-1]-1],
则根据next定义得B[ex[i-1]-j..ex[i-1]-1]==B[0..j-1],又因为A[i-ex[i-1]..i-
1]==B[0..ex[i-1]-1]得A[i-j..i-1]==B[ex[i-1]-j..ex[i-1]-1],这样有A[i-j..i-1]==B[0..j-1]
也就是此时只需再比较A[i]与B[j]的值是否相等即可,若相等,可得ex[i]=j+1,若仍不相等,则更新j为next[j-1],继续比较
A[i]与B[j]是否相等……直到A[i]与B[j]相等或直到j==0时,A[i]仍不等于B[j],此时ex[i]=0。边界:求ex[0]时,初
始j(用来代替ex[i-1])为0。
现在还有一个问题,如何求next?显然next就是以B自身为模板串,B为子串的“自身匹配”,用类似的办法即可,唯一不同的是next[0]=lenB可以直接得到,求next[1]时,初始j(代替next[i-1])为0。

     lenA = strlen(A); lenB = strlen(B);
next[] = lenB;
int j = ;
re2(i, , lenB) {
while (j && B[i] != B[j]) j = next[j - ];
if (B[i] == B[j]) j++;
next[i] = j;
}
j = ;
re(i, lenA) {
while (j && A[i] != B[j]) j = next[j - ];
if (A[i] == B[j]) j++;
ex[i] = j;
}

核心代码

扩展KMP:给出模板串A和子串B,长度分别为lenA和lenB,要求在线性时间内,对于每个A[i](0<=i< lenA),求出A[i..lenA-1]与B的最长公共前缀长度,记为ex[i](或者说,ex[i]为满足A[i..i+z-1]==B[0..z- 1]的最大的z值)。扩展KMP可以用来解决很多字符串问题,如求一个字符串的最长回文子串和最长重复子串。
【算法】
设next[i]为满足B[i..i+z-1]==B[0..z-1]的最大的z值(也就是B的自身匹配)。设目前next[0..lenB-1]与ex[0..i-1]均已求出,要用它们来求ex[i]的值。

p为目前A串中匹配到的最远位置,k为让其匹配到最远位置的值(或者说,k是在0<=i0<i的所有i0值中,使i0+ex[i0]-1的值
最大的一个,p为这个最大值,即k+ex[k]-1),显然,p之后的所有位都是未知的,也就是目前还无法知道A[p+1..lenA-1]中的任何一位
和B的任何一位是否相等。
根据ex的定义可得,A[k..p]==B[0..p-k],因为i>k,所以又有
A[i..p]==B[i-k..p-k],设L=next[i-k],则根据next的定义有B[0..L-1]==B[i-k..i-k+L-1]。
考虑i-k+L-1与p-k的关系:
(1)i-k+L-1<p-k,即i+L<=p。这时,由
A[i..p]==B[i-k..p-k]可以得到A[i..i+L-1]==B[i-k..i-k+L-1],又因为B[0..L-1]==B[i-
k..i-k+L-1]所以A[i..i+L-1]==B[0..L-1],这就说明ex[i]>=L。又由于next的定义可得,A[i+L]必
然不等于B[L](否则A[i..i+L]==B[0..L],因为i+L<=p,所以A[i..i+L]==B[i-k..i-k+L],这样
B[0..L]==B[i-k..i-k+L],故next[i-k]的值应为L+1或更大),这样,可以直接得到ex[i]=L!
(2)i+k-
L+1>=p-k,即i+L>p。这时,首先可以知道A[i..p]和B[0..p-i]是相等的(因为A[i..p]==B[i-
k..p-k],而i+k-L+1>=p-k,由B[0..L-1]==B[i-k..i-k+L-1]可得B[0..p-i]==B[i-
k..p-k],即A[i..p]==B[0..p-i]),然后,对于A[p+1]和B[p-i+1]是否相等,目前是不知道的(因为前面已经说过,p
是目前A串中匹配到的最远位置,在p之后无法知道任何一位的匹配信息),因此,要从A[p+1]与B[p-i+1]开始往后继续匹配(设j为目前B的匹配
位置的下标,一开始j=p-i+1,每次比较A[i+j]与B[j]是否相等,直到不相等或者越界为止,此时的j值就是ex[i]的值)。在这种情况
下,p的值必然会得到延伸,因此更新k和p的值。
边界:ex[0]的值需要预先求出,然后将初始的k设为0,p设为ex[0]-1。
对于求next数组,也是“自身匹配”,类似KMP的方法处理即可。唯一的不同点也在边界上:可以直接知道next[0]=lenB,next[1]的值预先求出,然后初始k=1,p=ex[1]。


要严重注意的是,在上述的情况(2)中,本该从A[p+1]与B[p-i+1]开始匹配,但是,若p+1<i,也就是p-i+1<0(这种情
况是有可能发生的,当ex[i-1]=0,且前面的ex值都没有延伸到i及以后的时候)的话,需要将A、B的下标都加1(因为此时p必然等于i-2,如果
A、B的下标用两个变量x、y控制的话,x和y都要加1)!!

 lenA = strlen(A); lenB = strlen(B);
next[] = lenB; next[] = lenB - ;
re(i, lenB-) if (B[i] != B[i + ]) {next[] = i; break;}
int j, k = , p, L;
re2(i, , lenB) {
p = k + next[k] - ; L = next[i - k];
if (i + L <= p) next[i] = L; else {
j = p - i + ;
if (j < ) j = ;
while (i + j < lenB && B[i + j] == B[j]) j++;
next[i] = j; k = i;
}
}
int minlen = lenA <= lenB ? lenA : lenB; ex[] = minlen;
re(i, minlen) if (A[i] != B[i]) {ex[] = i; break;}
k = ;
re2(i, , lenA) {
p = k + ex[k] - ; L = next[i - k];
if (i + L <= p) ex[i] = L; else {
j = p - i + ;
if (j < ) j = ;
while (i + j < lenA && j < lenB && A[i + j] == B[j]) j++;
ex[i] = j; k = i;
}
}

核心代码

【时间复杂度分析】
在KMP和扩展KMP中,不管是A串还是B串,其匹配位置都是单调递增的,故总时间复杂度是线性的,都为O(lenA + lenB)(只是扩展KMP比KMP的常数更大一些)。
【应用】
KMP和扩展KMP在解决字符串问题中有大用。很多看上去很猥琐的字符串问题,都可以归结到这两种算法之中。另外,这里的“字符串”可以延伸为一切类型的数组,而不仅仅是字符数组。

KMP与扩展KMP的更多相关文章

  1. KMP和扩展KMP【转】

    这种东西基本上在纸上自己推导一下就能做出来XD 转发注明出处 KMP 给出两个字符串A(称为模板串)和B(称为子串),长度分别为lenA和lenB,要求在线性时间内,对于每个A[i] (0<=i ...

  2. Manacher模板,kmp,扩展kmp,最小表示法模板

    *N]; //储存临时串 *N];//中间记录 int Manacher(char tmp[]) { int len=strlen(tmp); ; ;i<len;i++) { s[cnt++]= ...

  3. KMP && Manacher && 扩展KMP整理

    KMP算法: kmp示例代码: void cal_next(char *str, int *next, int len) { next[0] = -1;//next[0]初始化为-1,-1表示不存在相 ...

  4. KMP和扩展KMP

    文章网上太多这里提一下代码细节: KMP: scanf("%s\n",s); scanf("%s\n",t); int ls=strlen(s),lt=strl ...

  5. kmp模板 && 扩展kmp模板

    kmp模板: #include <bits/stdc++.h> #define PB push_back #define MP make_pair using namespace std; ...

  6. 【kmp或扩展kmp】HDU 6153 A Secret

    acm.hdu.edu.cn/showproblem.php?pid=6153 [题意] 给定字符串A和B,求B的所有后缀在A中出现次数与其长度的乘积之和 A和B的长度最大为1e6 方法一:扩展kmp ...

  7. KMP 、扩展KMP、Manacher算法 总结

    一. KMP 1 找字符串x是否存在于y串中,或者存在了几次 HDU1711 Number Sequence HDU1686 Oulipo HDU2087 剪花布条 2.求多个字符串的最长公共子串 P ...

  8. 666 专题三 KMP &#38; 扩展KMP &#38; Manacher

    KMP: Problem A.Number Sequence d.求子串首次出现在主串中的位置 s. c. #include<iostream> #include<stdio.h&g ...

  9. kmp与扩展kmp模板

    kmp 1 #include <algorithm> 2 #include <iostream> 3 #include <cstring> 4 #include & ...

随机推荐

  1. (转) Resource file and Source file

    基本上是这样的,Sourcefile文件夹里面放的是CPP文件这些,Resourcefile文件夹是资源文件夹,里面可以放你程序里需要的资源,包括图标,对话框,图片等等:对应的文件如下: Source ...

  2. web app之rem

    rem是什么? rem:font size of the root element,是指相对于根元素的字体大小的单位.简单的说它就是一个相对单位. em:font size of the elemen ...

  3. 列表:一个打了激素的数组2 - 零基础入门学习Python011

    列表:一个打了激素的数组2 让编程改变世界 Change the world by program 从列表中获取元素 跟数组一样,我们可以通过元素的索引值(index)从列表获取单个元素,注意,列表索 ...

  4. android 利用Bitmap获取圆角矩形、圆形图片

    1.在很多时候,我们要显示图片资源,需要将他的资源显示为圆角的:示例源码如下: public static Bitmap getRoundedCornerBitmap(Bitmap bitmap,fl ...

  5. mysql 套事物实例

    public static DataSet GetPPriceList(string aircompany, string departPort, string arrivePort, string ...

  6. [OpenJudge] 百练2754 八皇后

    八皇后 Description 会下国际象棋的人都很清楚:皇后可以在横.竖.斜线上不限步数地吃掉其他棋子.如何将8个皇后放在棋盘上(有8 * 8个方格),使它们谁也不能被吃掉!这就是著名的八皇后问题. ...

  7. android添加edittext后布局就不好用

    在布局添加控件手动添加还是拖的添加,添加edittext后布局就不好用,其他控件好用,然后就说下面这段话 Exception raised during rendering: java.lang.Sy ...

  8. Win7/Win8/Win8.1众多版本,我该选择哪个?

    当你要下载Win7或者Win8/8.1镜像时,是不是被Windows版本种类给吓着了?到底该选择哪种版本的?其实,大多数人用的就那一两个版本,这也是推荐选择的版本,请看快速通道.如果你想了解的更多一点 ...

  9. GStreamer Plugin: Embedded video playback halted; module decodebin20 reported: Your GStreamer installation is missing a plug-in.

    标题是在Linux下使用系统yum install 的opencv库来获取视频帧的时候抛出来的错误消息.opencv调用了Gstream的API来处理了视频.错误抛出的代码如下图: http://ub ...

  10. centos 6.5 hadoop 2.3 初配置

    为了安装hadoop废了好大的劲才把esxi5.5给装好. 同时装了centos6.5,由于hadoop里面有个免密码登陆所以这里讲的就是免密码登陆. 看了大家的博客文章发现转发的一部分,写ubunt ...