KMP 求最小循环节
转载自:https://www.cnblogs.com/chenxiwenruo/p/3546457.html
下面是有关学习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 = 0;
next[i] = -1;
int j = -1;
while( i < len )
{
if( j == -1 || 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=0,j=0; //i为target中字符的下标,j为str中字符的下标
int cnt=0; //统计str字符串在target字符串中出现的次数
while(i<=n-1){
if(j<0||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=0,j=0; //i为target中字符的下标,j为str中字符的下标
int cnt=0; //统计str字符串在target字符串中出现的次数
while(i<n && j<m){
if(j<0||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 -1;
}
版本二(算法导论):
//这里的next和前面一样,next[i]就是前面长度为i的字符串前缀和后缀相等的长度,
//即索引为i的字符失配时的前缀函数
void getNext(char *str,int m){
memset(next,0,sizeof(next));
next[1]=0;
int k=0;
for(int i=2;i<=m;i++){
while(k>0 && str[k]!=str[i-1])
k=next[k];
if(str[k]==str[i-1])
k++;
next[i]=k;
}
}
//n为target字符串的长度,m为str字符串的长度,统计str在target中出现的个数
int match(char *target,int n,char * str,int m){
int k=0,cnt=0;
for(int i=0;i<n;i++){
while(k>0 && 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=0,cnt=0;
for(int i=0;i<n;i++){
while(k>0 && str[k]!=target[i])
k=next[k];
if(str[k]==target[i])
k++;
if(k==m){
return i-m+1;
}
}
return -1;
}
某大神的模板(其实和算法导论一样):
#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[10010],i,k,c;
n=strlen(T);m=strlen(P);
next[1]=k=0;
for(i=1;i<m;i++){
KMP_GO(P);
next[i+1]=k;//这里i表示的是字符的索引,对应的长度i+1
}
k=c=0;
for(i=0;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
一道求循环节的题目
题目链接:https://oj.ismdeep.com/contest/Problem?id=1284&pid=6
G: Dave的时空迷阵
Submit
Problem Description
皇家理工本部隐藏着一座扭曲时空的迷阵,一旦陷入迷阵就不能复出。Dave作为一个勇敢的探险家,勇敢闯入迷阵,并发现了一些规律……
Dave发现总是在行进一定距离后回到起点,继续走上重复的路途….
冷静分析之后,Dave在前进的路途中记录了标记(a-z的小写字母),并得到了一个字符串,Dave想知道,从起点开始,最少走多远会回到初始状态?
Input
第一行一个正整数nn为Dave记录的字符串长度(1≤n≤2×105)(1≤n≤2×105)
第二行为长度nn的字符串,仅包含a−za−z的小写英文字母的非空字符串
Output
第一行输出从起点再到起点的距离
第二行输出行进路上遇到的字符
Sample Input
4
abcd
Sample Output
4
abcd 解题思路:对于给定串求出 next 数组,利用循环节性质得出是否满足循环,再将末尾多余的字母隔过 去,输出一个整的循环节即可 附上代码:
#include <bits/stdc++.h>
using namespace std;
const int AX = 3e5+;
char ps[AX];
int len ;
int next1[AX];
void getnext1( int m )
{
next1[] = -;
int j = ;
int k = -;
while ( j < m )
{
if (k == - || ps[j] == ps[k])
next1[++j] = ++k;
else
k = next1[k];
}
}
int main()
{
scanf("%d",&len);
scanf("%s",ps);
getnext1(len); int ans = len - next1[len];
int left = next1[len] % ans ;
printf("%d\n",ans);
for( int i = len - left - ans ; i < len - left ; i ++ )
printf("%c",ps[i]);
printf("\n");
return ;
}
KMP 求最小循环节的更多相关文章
- poj2406--Power Strings(KMP求最小循环节)
Power Strings Time Limit: 3000MS Memory Limit: 65536K Total Submissions: 33178 Accepted: 13792 D ...
- 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 ...
- hdu 3746 Cyclic Nacklace (KMP求最小循环节)
//len-next[len]为最小循环节的长度 # include <stdio.h> # include <algorithm> # include <string. ...
- Hdu 1358 Period (KMP 求最小循环节)
题目链接: http://acm.hdu.edu.cn/showproblem.php?pid=1358 题目描述: 给出一个字符串S,输出S的前缀能表达成Ak的所有情况,每种情况输出前缀的结束位置和 ...
- POJ 2406 - Power Strings - [KMP求最小循环节]
题目链接:http://poj.org/problem?id=2406 Time Limit: 3000MS Memory Limit: 65536K Description Given two st ...
- Power Strings POJ2406 KMP 求最小循环节
相比一般KMP,构建next数组需要多循环一次,因为next[j]代表前j-1个字符的最长相同前缀后缀,比如字符串为aab aab aab共9个字符,则next[10]等于前9个字符中最长相同前缀后缀 ...
- poj1961--Period(KMP求最小循环节)
Period Time Limit: 3000MS Memory Limit: 30000K Total Submissions: 13511 Accepted: 6368 Descripti ...
- HDU-3746 Cyclic Nacklace 字符串匹配 KMP算法 求最小循环节
题目链接:https://cn.vjudge.net/problem/HDU-3746 题意 给一串珠子,我们可以在珠子的最右端或最左端加一些珠子 问做一条包含循环珠子的项链,最少还需要多少珠子 思路 ...
- The Minimum Length - HUST 1010(求最小循环节)
题意:有个一字符串A(本身不是循环串),然后经过很多次自增变成AAAAA,然后呢从自增串里面切出来一部分串B,用这个串B求出来A的长度. 分析:其实就是求最小循环节.......串的长度 - 最大 ...
随机推荐
- Setting property 'source' to 'org.eclipse.jst.jee.server:hczm' did not find a matching property
- css 别人找的css特效
https://blog.csdn.net/m0_37809478/article/details/76619207
- C# Note32: 查漏补缺
(1)Using的三种使用方式 (2)C#详解值类型和引用类型区别 (3)c#中字段(field)和属性(property)的区别 (4)C#中的 int? int?:表示可空类型,就是一种特殊的值类 ...
- Python 爬虫 解析库的使用 --- XPath
一.使用XPath XPath ,全称XML Path Language,即XML路径语言,它是一门在XML文档中查找信息的语言.它最初是用来搜寻XML文档的,但是它同样适用于HTML文档的搜索. 所 ...
- hadoop 管理命令dfsadmin
hadoop 管理命令dfsadmin dfsadmin 命令用于管理HDFS集群,这些命令常用于管理员. 1. (Safemode)安全模式 动作 命令 把集群切换到安全模式 bin/hdfs df ...
- 进阶开发——文档,缓存,ip限速
一.文档自动化管理 1.django rest framework提供了一个接口: 可以将代码中注释转换为文档中内容(list,create等),以及help_text等等,且会生成JavaScrip ...
- python3 自动识图
一.安装依赖库 pip install pytesseract pip install pillow 二.安装识图引擎tesseract-ocr https://pan.baidu.com/s/1Qa ...
- Shell 编程和Python编程的那些不同之处(一)
循环 shell中for循环的表现形式: 1.数字段形式 for i in {1..10};do echo $i;done 还支持按规定的步数进行跳跃的方式实现列表for循环,例如计算1-100内所 ...
- SQL约束(主键约束、外键约束、自动递增、不允许空值、值唯一、值默认、值限制范围)
NOT NULL 不允许空值约束 NOT NULL 约束强制列不接受 NULL 值(NULL值就是没有值或缺值).NOT NULL 约束强制字段始终包含值,即不向字段添加值,就无法插入新记录或者更新记 ...
- 一、关于a标签伪类中的visited不起作用问题
一.代码示范 <html> <head> <title>伪类超链接</title> <!--<link href="./test. ...