(可以转载,但请注明出处!)

下面是有关学习KMP的参考网站

http://blog.csdn.net/yaochunnian/article/details/7059486

http://blog.csdn.net/v_JULY_v/article/details/6111565

http://blog.csdn.net/v_JULY_v/article/details/6545192

http://blog.csdn.net/oneil_sally/article/details/3440784

http://billhoo.blog.51cto.com/2337751/411486

先说说next数组的含义:

next[i]就是前面长度为i的字符串前缀和后缀相等的最大长度,也即索引为i的字符失配时的前缀函数。

下面几个版本的next函数,除了next[0]不同外(版本一中为-1,版本二中为0),其余无差别

一:KMP算法模板

版本一:

//求str对应的next数组
void getNext(char const* str, int len)
{
int i = ;
next[i] = -;
int j = -;
while( i < len )
{
if( j == - || str[i] == str[j] ) //循环的if部分
{
++i;
++j;
//修正的地方就发生下面这4行
if( str[i] != str[j] ) //++i,++j之后,再次判断ptrn[i]与ptrn[j]的关系
next[i] = j; //之前的错误解法就在于整个判断只有这一句。
else
next[i] = next[j]; //这里其实是优化了后的,也可以仍是next[i]=j
//当str[i]==str[j]时,如果str[i]匹配失败,那么换成str[j]肯定也匹配失败,
//所以不是令next[i]=j,而是next[i] = next[j],跳过了第j个字符,
//即省去了不必要的比较
//非优化前的next[i]表示前i个字符中前缀与后缀相同的最大长度
}
else //循环的else部分
j = next[j];
}
} //在目标字符串target中,字符str出现的个数
//n为target字符串的长度,m为str字符串的长度
int kmp_match(char *target,int n,char *str,int m){
int i=,j=; //i为target中字符的下标,j为str中字符的下标
int cnt=; //统计str字符串在target字符串中出现的次数
while(i<=n-){
if(j<||target[i]==str[j]){
i++;
j++;
}
else{
j=next[j]; //当j=0的时候,suffix[0]=-1,这样j就会小于0,所以一开始有判断j是否小于0
} //str在target中找到匹配
if(j==m){
cnt++;
j=next[j];
}
}
return cnt;
}
//在目标字符串target中,若存在str字符串,返回匹配成功的第一个字符的位置
int kmp_search(char *target,int n,char *str,int m){
int i=,j=; //i为target中字符的下标,j为str中字符的下标
int cnt=; //统计str字符串在target字符串中出现的次数
while(i<n && j<m){
if(j<||target[i]==str[j]){
i++;
j++;
}
else{
j=suffix[j]; //当j=0的时候,suffix[0]=-1,这样j就会小于0,所以一开始有判断j是否小于0
}
}
if(j>=m)
return i-m;
else
return -;
}

版本二(算法导论):

//这里的next和前面一样,next[i]就是前面长度为i的字符串前缀和后缀相等的长度,
//即索引为i的字符失配时的前缀函数
void getNext(char *str,int m){
memset(next,,sizeof(next));
next[]=;
int k=;
for(int i=;i<=m;i++){
while(k> && str[k]!=str[i-])
k=next[k];
if(str[k]==str[i-])
k++;
next[i]=k;
}
}
//n为target字符串的长度,m为str字符串的长度,统计str在target中出现的个数
int match(char *target,int n,char * str,int m){
int k=,cnt=;
for(int i=;i<n;i++){
while(k> && str[k]!=target[i])
k=next[k];
if(str[k]==target[i])
k++;
if(k==m){
cnt++;
k=next[k];
}
}
return cnt;
} //n为target字符串的长度,m为str字符串的长度
//若存在str字符串,返回匹配成功的第一个字符的位置
int match(char *target,int n,char * str,int m){
int k=,cnt=;
for(int i=;i<n;i++){
while(k> && str[k]!=target[i])
k=next[k];
if(str[k]==target[i])
k++;
if(k==m){
return i-m+;
}
}
return -;
}

某大神的模板(其实和算法导论一样):

#define KMP_GO(X) while(k>0 && P[k]!=X[i]) k=next[k];if(P[k]==X[i])k++
//求字符串P在T中出现的次数
int kmp_match(char*T,char*P){
int n,m,next[],i,k,c;
n=strlen(T);m=strlen(P);
next[]=k=;
for(i=;i<m;i++){
KMP_GO(P);
next[i+]=k;//这里i表示的是字符的索引,对应的长度i+1
}
k=c=;
for(i=;i<n;i++){
KMP_GO(T);
if(k==m){
c++;
k=next[k];
}
}
return c;
}

二:KMP最小循环节、循环周期:

定理:假设S的长度为len,则S存在最小循环节,循环节的长度L为len-next[len],子串为S[0…len-next[len]-1]。

(1)如果len可以被len - next[len]整除,则表明字符串S可以完全由循环节循环组成,循环周期T=len/L。

(2)如果不能,说明还需要再添加几个字母才能补全。需要补的个数是循环个数L-len%L=L-(len-L)%L=L-next[len]%L,L=len-next[len]。

理解该定理,首先要理解next数组的含义:next[i]表示前面长度为i的子串中,前缀和后缀相等的最大长度。

如:abcdabc

index

0

1

2

3

4

5

6

7

char

a

b

c

d

a

b

C

next

-1

0

0

0

0

1

2

3

如对于a,ab,abc,abcd,很明显,前缀和后缀相同的长度为0

对于长度为5的子串abcda,前缀的a和后缀的a相同,长度为1

对于长度为6的子串abcdab,前缀的ab和后缀的ab相同,长度为2

接下来举几个例子来说明最小循环节和循环周期:

为方便说明,先设字符串的长度为len,循环子串的长度为L

1.

s0s1s2s3s4s5 ,next[6]=3

即s0s1s2=s3s4s5

很明显可知:循环子串为s0s1s2,L=len-next[6]=3,且能被len整除。

2.

s0s1s2s3s4s5s6s7 ,next[8]=6

此时len-next[8]=2 ,即L=2

由s0s1s2s3s4s5=s2s3s4s5s6s7

可知s0s1=s2s3,s2s3=s4s5,s4s5=s6s7

显然s0s1为循环子串

3.

s0s1s2s3s4s5s6 ,next[7]=4

此时len-next[7]=3,即L=3

由s0s1s2s3=s3s4s5s6

可知s0s1=s3s4,s2s3=s5s6

从而可知s0s1s2=s3s4s5,s0=s3=s6

即如果再添加3-4%3=2个字母(s1s2),那么得到的字符串就可以由s0s1s2循环3次组成

这个定理可以这么理解:

http://www.cnblogs.com/oyking/p/3536817.html

对于一个字符串,如abcd abcd abcd,由长度为4的字符串abcd重复3次得到,那么必然有原字符串的前八位等于后八位。

也就是说,对于某个字符串S,长度为len,由长度为L的字符串s重复R次得到,当R≥2时必然有S[0..len-L-1]=S[L..len-1],字符串下标从0开始

那么对于KMP算法来说,就有next[len]=len-L。此时L肯定已经是最小的了(因为next的值是前缀和后缀相等的最大长度,即len-L是最大的,那么在len已经确定的情况下,L是最小的)。

如果一定仔细证明的话,请看下面:

(参考来自:http://www.cnblogs.com/wuyiqi/archive/2012/01/06/2314078.html,有所改动)

k    m   x     j     i

由上,next【i】=j,两段红色的字符串相等(两个字符串完全相等),s[k....j]==s[m....i]

设s[x...j]=s[j....i](xj=ji)

则可得,以下简写字符串表达方式

kj=kx+xj;

mi=mj+ji;

因为xj=ji,所以kx=mj,如下图所示

k   m     a    x    j     i

设s[a…x]=s[x..j](ax=xj)

又由xj=ji,可知ax=xj=ji

即s[a…i]是由s[a…x]循环3次得来的。

而且看到没,此时又重复上述的模型,s[k…x]=s[m…j],可以一直递推下去

最后可以就可以递推出文章开头所说的定理了。

最后再举两个相关例子

abdabdab  len:8 next[8]:5

最小循环节长度:3(即abd)   需要补的个数是1  d

ababa  len:5 next[5]:3

最小循环节长度:2(即ab)    需要补的个数是1  b

KMP模板,最小循环节的更多相关文章

  1. KMP 求最小循环节

    转载自:https://www.cnblogs.com/chenxiwenruo/p/3546457.html KMP模板,最小循环节   下面是有关学习KMP的参考网站 http://blog.cs ...

  2. UVA 10298 Power Strings 字符串的幂(KMP,最小循环节)

    题意: 定义a为一个字符串,a*a表示两个字符相连,即 an+1=a*an ,也就是出现循环了.给定一个字符串,若将其表示成an,问n最大为多少? 思路: 如果完全不循环,顶多就是类似于abc1这样, ...

  3. poj2406--Power Strings(KMP求最小循环节)

    Power Strings Time Limit: 3000MS   Memory Limit: 65536K Total Submissions: 33178   Accepted: 13792 D ...

  4. HDU 3746 - Cyclic Nacklace & HDU 1358 - Period - [KMP求最小循环节]

    题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=3746 Time Limit: 2000/1000 MS (Java/Others) Memory Li ...

  5. hdu 3746 Cyclic Nacklace (KMP求最小循环节)

    //len-next[len]为最小循环节的长度 # include <stdio.h> # include <algorithm> # include <string. ...

  6. POJ 1961 2406 (KMP,最小循环节,循环周期)

    关于KMP的最短循环节.循环周期,请戳: http://www.cnblogs.com/chenxiwenruo/p/3546457.html (KMP模板,最小循环节) POJ 2406  Powe ...

  7. Hdu 1358 Period (KMP 求最小循环节)

    题目链接: http://acm.hdu.edu.cn/showproblem.php?pid=1358 题目描述: 给出一个字符串S,输出S的前缀能表达成Ak的所有情况,每种情况输出前缀的结束位置和 ...

  8. POJ 2406 - Power Strings - [KMP求最小循环节]

    题目链接:http://poj.org/problem?id=2406 Time Limit: 3000MS Memory Limit: 65536K Description Given two st ...

  9. Power Strings POJ2406 KMP 求最小循环节

    相比一般KMP,构建next数组需要多循环一次,因为next[j]代表前j-1个字符的最长相同前缀后缀,比如字符串为aab aab aab共9个字符,则next[10]等于前9个字符中最长相同前缀后缀 ...

  10. poj1961--Period(KMP求最小循环节)

    Period Time Limit: 3000MS   Memory Limit: 30000K Total Submissions: 13511   Accepted: 6368 Descripti ...

随机推荐

  1. Ajax 异步调用代码

    function jsAjax() { var Con; var XmlRequset; var AjaxContent; //返回内容 if (window.XMLHttpRequest) { // ...

  2. iFreeThinking - 记录生活,分享思考

    http://www.ifreethinking.com iFreeThinking.com 是一个非营利性个人博客网站.开于 2014 年,博客主要记录分享一些思考和感悟. 文章列表:http:// ...

  3. ED/EP系列1《简介》

    电子存折(ED:ElectronicDeposit)一种为持卡人进行消费.取现等交易而设计的支持个人识别码(PIN)保护的金融IC卡应用.它支持圈存.圈提.消费和取现等交易. 电子钱包(EP:Elec ...

  4. Tutorial: Facebook analytics using Power BI Desktop

    In this tutorial you learn how to import and visualize data from Facebook. During the tutorial you'l ...

  5. 怎么解决/bin/sh: arm-linux-gcc: not found make

      1.arm-linux-gcc 环境变量没有设,所以找不到这个编译器 在/etc/profile里添加arm-linux-gcc的存放路径 sudo -s gedit /etc/profile 编 ...

  6. 调整maven配置文件

    maven的配置文件位置:maven安装位置\conf\settings.xml. 这次调整maven的配置文件主要解决三个问题: 调整本地依赖库位置 设置代理 添加远程资源库镜像节点 调整本地依赖库 ...

  7. [转]ubuntu 14.04 系统设置不见了

    [转]ubuntu 14.04 系统设置不见了 http://blog.sina.com.cn/s/blog_6c9d65a10101i0i7.html 不知道删除什么了,系统设置不见了! 我在终端运 ...

  8. Ant学习---第一节:Ant安装和简单使用

    一.下载 ant 插件,路径如下: http://ant.apache.org/bindownload.cgi 二.安装 ant 插件,解压下载下来的 ant 插件,配置环境变量(最好系统环境变量), ...

  9. 网络---中断套接字Socket

    package socketpack_2; import java.awt.BorderLayout; import java.awt.EventQueue; import java.awt.even ...

  10. 【Populating Next Right Pointers in Each Node II】cpp

    题目: Follow up for problem "Populating Next Right Pointers in Each Node". What if the given ...