对于kmp求next数组的理解
首先附上代码
1 void GetNext(char* p,int next[])
2 {
3 int pLen = strlen(p);
4 next[0] = -1;
5 int k = -1;
6 int j = 0;
7 while (j < pLen - 1)
8 {
9 //p[k]表示前缀,p[j]表示后缀
10 if (k == -1 || p[j] == p[k])
11 {
12 ++k;
13 ++j;
14 next[j] = k;
15 }
16 else
17 {
18 k = next[k];
19 }
20 }
21 }
首先我们得明白,next[j]是表示除了p[j]之外,0~j-1这个串,前缀和后缀的最大匹配长度
因为我们下标是从0开始,所以这个最大匹配长度也就是,满足最长的前缀和后缀匹配后的前缀的下一个位置,
所以next数组满足如下性质
对于0~j
p[j-1]=p[next[j]-1],为什么呢,因为next[j]表示,和以p[j-1]为最后一个字符的后缀匹配的最长前缀的下一个位置,
那么0~next[j]这个串最后的位置的前一个位置一定和后缀的最后一个位置p[j-1]匹配,所以p[next[j]-1]=p[j-1]
那么对于0~next[j]
p[next[j]-1]=p[next[next[j]]-1]
next[next[j]]表示,和以p[next[j]-1]为最后一个字符的后缀匹配后的最长前缀的下一个位置,(最长的意思就是下一个位置一定不匹配,如果下一个位置匹配,那么这个就不是最长),那么对于0~next[next[j]],它最后一个位置的前一个位置一定和后缀的最后一个位置p[next[j]-1]相等
于是
p[next[next[j]-1]]=p[next[j]-1]
于是我们能推出什么呢
p[j-1]=p[next[j]-1]=p[next[next[j]]-1]
也就是说记最初的位置为j,迭代若干次next数组(j=next[j])得到j'
一定满足p[j-1]=p[j'-1]
另外对于上面的代码,首先我们分析每个位置的状态
如果它的位置为0,那么当不匹配的时候它将陷入一个自环,不断地只能和第一个0位置匹配,所以我们设计
0->-1->0 这样的自环状态
那么对于位置不为0的地方,有两种情况
找不到相同的前缀和后缀,如果当前的位置为j,则说明此时没有能匹配的满足以p[j-1]为最后一个字符的后缀,那么怎么办呢,那就只能暴力回溯
到第0个位置
能找到相同的前缀后缀
此时又分两种情况,能匹配上
如果能匹配上则,下一个位置的next就等于现在的k+1,因为此时的k其实等于next[j],如果p[j]==p[next[j]],那么next[j+1]=next[j]+1
因为next[j]没有算p[j],此时如果再次匹配则说明前后缀公共长多应当多加上代表新加进来的字符p[j]的一个长度
详细一点说就是当前的最长匹配前缀从0~next[j]-1,变成了0~next[j]
不能匹配上
我们令j=next[j],由上面的结论推知p[j-1]=p[j'-1],所以我们能得到长度不断递减的,能匹配以p[j-1]为最后一个字符的后缀
如果k回溯到-1,那说明确实没有相同的前后缀,放弃之前保留的匹配长度,直接将位置回溯到0
实际上不省略内在逻辑的求Next数组的程序是
void initNext(){
Next[1]=0;
int p1=0,p2=1;
while(p2<=m){
if(p1==0){
//此时说明它连第一个字符都匹配不了,那么后续的匹配应当让前缀的指针停留在第一个字符位置1
//以p2为结尾的后缀没有匹配的前缀
Next[p2+1]=1;//那么当它的下一位失配应当比较第一位
//这里的含义非常特殊,因为第1位之前的串是空串,也即其实符合了Next数组的定义,空串的最长前后匹配是0
//比较第一位也就意味着,第一位之前的空串的最大前后匹配是0
//此处也表示最长前后匹配为0时,只能将指针回溯到第一位,也即第一位还没有匹配,等待匹配的状态
p1=1;//回溯指针,p1悬浮在可能扩展的最后一个字符
p2++;//考虑计算以p2+1结尾的最大匹配
}
else if(b[p1]==b[p2]){
//当前缀和后缀有一个字符匹配
Next[p2+1]=Next[p2]+1;//这个转移表示从1~Next[p2]-1的匹配串延拓到1~Next[p2]
p1=Next[p2]+1;//p1要移动到当前可能扩展的最后一个字符
p2++;//考虑计算以p2+1结尾的最大匹配
}
else{
//第一次进入这个状态时,或者连续这个状态迭代的第一次,当前的p1其实就等于Next[p2]
//Next[p2]表示,1~Next[p2]-1,b[Next[p2]-1]=b[p2-1];
//p1=Next[p1]就等价于p1=Next[Next[p2]];
//仍然能得到一个b[Next[Next[p2]]-1]=b[Next[p2]-1]=b[p2-1]的前缀
p1=Next[p1];//不断迭代到长度递减前缀,符合匹配最后一个字符是b[p1-1]
}
}
}
并且值得我们注意的是,
指针p1悬浮在的位置说明该位置状态不确定,需要匹配一下确认
p1=minIndex-1时则表示,连第一个字符都匹配不到,应当走自环,让指针p1再回到minIndex,因为此时minIndex位置待匹配
长度为len的串,则长度为len的前缀和后缀一定相等就是它本身,此时最大匹配长度不计数,因为它是没有意义的
比如a,其实它有前缀a,后缀a但由于长度为len所以不计数
所以前缀指针p1,后缀指针p2,初始的时候p2-p1=1也即相邻,也就是说初始最小长度一定是2,一个在前一个在后
hdu1711求模式串在文本串中出现的最早位置
#include <iostream>
#include <cstdio>
#include <cstring>
using namespace std;
const int maxn=1e5+7;
const int N=1e6+7;
int Next[maxn],a[N],b[maxn],n,m;
void initNext(){
memset(Next,0,sizeof(Next));
Next[1]=0;
int p1=0,p2=1;
while(p2<=m){
if(p1==0){
//此时说明它连第一个字符都匹配不了,那么后续的匹配应当让前缀的指针停留在第一个字符位置1
//以p2为结尾的后缀没有匹配的前缀
Next[p2+1]=1;//那么当它的下一位失配应当比较第一位
//这里的含义非常特殊,因为第1位之前的串是空串,也即其实符合了Next数组的定义,空串的最长前后匹配是0
//比较第一位也就意味着,第一位之前的空串的最大前后匹配是0
//此处也表示最长前后匹配为0时,只能将指针回溯到第一位,也即第一位还没有匹配,等待匹配的状态
p1=1;//回溯指针,p1悬浮在可能扩展的最后一个字符
p2++;//考虑计算以p2+1结尾的最大匹配
}
else if(b[p1]==b[p2]){
//当前缀和后缀有一个字符匹配
Next[p2+1]=Next[p2]+1;//这个转移表示从1~Next[p2]-1的匹配串延拓到1~Next[p2]
p1=Next[p2]+1;//p1要移动到当前可能扩展的最后一个字符
p2++;//考虑计算以p2+1结尾的最大匹配
}
else{
//第一次进入这个状态时,或者连续这个状态迭代的第一次,当前的p1其实就等于Next[p2]
//Next[p2]表示,1~Next[p2]-1,b[Next[p2]-1]=b[p2-1];
//p1=Next[p1]就等价于p1=Next[Next[p2]];
//仍然能得到一个b[Next[Next[p2]]-1]=b[Next[p2]-1]=b[p2-1]的前缀
p1=Next[p1];//不断迭代到长度递减前缀,符合匹配最后一个字符是b[p1-1]
}
}
}
int Match(){
int i,j;i=1;j=1;
while(i<=n){
if(j==0){
i++;j++;
}
else if(a[i]==b[j]){
i++;j++;
}
else j=Next[j];
if(j==m+1) return i-j+1;
}
return -1;
}
int main(){
int T;scanf("%d",&T);
while(T--){
scanf("%d%d",&n,&m);
for(int i=1;i<=n;++i) scanf("%d",a+i);
for(int i=1;i<=m;++i) scanf("%d",b+i);
initNext();
//for(int i=1;i<=m;++i) printf("nxt:%d,",Next[i]);printf("\n");
printf("%d\n",Match());
}
return 0;
}
但是其实这个程序是不对的,Next[p2+1]=Next[p2]+1;这个转移不对
真正的转移是 Next[p2]=i+1;此时i才是若干次迭代后的Next[p2']
所以对于poj1961,上面的求法就WA了,能过数据只是数据太水
Time Limit: 3000MS | Memory Limit: 30000K | |
Total Submissions: 17771 | Accepted: 8562 |
Description
Input
number zero on it.
Output
Sample Input
3
aaa
12
aabaabaabaab
0
Sample Output
Test case #1
2 2
3 3 Test case #2
2 2
6 2
9 3
12 4
Source
#include <iostream>
#include <cstdio>
#include <cstring>
using namespace std;
const int maxn=1e6+7;
char s[maxn];int n,Next[maxn]; int main(){
int cas=0;
while(~scanf("%d",&n)){
if(n!=0&&cas) printf("\n");
if(n==0) break;
printf("Test case #%d\n",++cas);
scanf("%s",s+1);
memset(Next,-1,sizeof(Next));
Next[1]=0;int i=0,j=1;
while(j<=n){
if(i==0){
Next[j+1]=1;//1 not 0
i=1;j++;
if(Next[j]!=-1&&j>=2&&j<=n&&j%(j-Next[j])==0&&s[j]==s[Next[j]]){
printf("%d %d\n",j,j/(j-Next[j]));
}
}
else if(s[i]==s[j]){
Next[j+1]=i+1;//
i++;j++;
if(Next[j]!=-1&&j>=2&&j<=n&&j%(j-Next[j])==0&&s[j]==s[Next[j]]){
printf("%d %d\n",j,j/(j-Next[j]));
}
}
else {
i=Next[i];//not j=Next[j]
}
}
}
return 0;
}
另外,有一个逻辑错,周期串不一定是2的倍数
Time Limit: 3000MS | Memory Limit: 65536K | |
Total Submissions: 48116 | Accepted: 20030 |
Description
Input
Output
Sample Input
abcd
aaaa
ababab
.
Sample Output
1
4
3
Hint
#include <iostream>
#include <cstring>
#include <cstdio>
using namespace std;
const int maxn=1e6+7;
char s[maxn];int Next[maxn];
int main(){
while(~scanf("%s",s+1)){
int len=strlen(s+1);
if(len==1){
if(s[1]=='.') break;
printf("1\n");continue;
}
memset(Next,-1,sizeof(Next));
Next[1]=0;int p1=0,p2=1;
while(p2<=len){
if(p1==0){
Next[++p2]=1;
p1=1;
}
else if(s[p1]==s[p2]){
Next[++p2]=p1+1;
p1++;
}
else p1=Next[p1];
}
if(Next[len]==1){
printf("1\n");continue;
}
if((len%(len-Next[len])==0)&&s[len]==s[Next[len]]){
printf("%d\n",len/(len-Next[len]));
}
else printf("1\n");
}
return 0;
}
Time Limit: 1000MS | Memory Limit: 65536K | |
Total Submissions: 40122 | Accepted: 16122 |
Description
The French author Georges Perec (1936–1982) once wrote a book, La disparition, without the letter 'e'. He was a member of the Oulipo group. A quote from the book:
Tout avait Pair normal, mais tout s’affirmait faux. Tout avait Fair normal, d’abord, puis surgissait l’inhumain, l’affolant. Il aurait voulu savoir où s’articulait l’association qui l’unissait au roman : stir son tapis, assaillant à tout instant son imagination, l’intuition d’un tabou, la vision d’un mal obscur, d’un quoi vacant, d’un non-dit : la vision, l’avision d’un oubli commandant tout, où s’abolissait la raison : tout avait l’air normal mais…
Perec would probably have scored high (or rather, low) in the following contest. People are asked to write a perhaps even meaningful text on some subject with as few occurrences of a given “word” as possible. Our task is to provide the jury with a program that counts these occurrences, in order to obtain a ranking of the competitors. These competitors often write very long texts with nonsense meaning; a sequence of 500,000 consecutive 'T's is not unusual. And they never use spaces.
So we want to quickly find out how often a word, i.e., a given string, occurs in a text. More formally: given the alphabet {'A', 'B', 'C', …, 'Z'} and two finite strings over that alphabet, a word W and a text T, count the number of occurrences of W in T. All the consecutive characters of W must exactly match consecutive characters of T. Occurrences may overlap.
Input
The first line of the input file contains a single number: the number of test cases to follow. Each test case has the following format:
- One line with the word W, a string over {'A', 'B', 'C', …, 'Z'}, with 1 ≤ |W| ≤ 10,000 (here |W| denotes the length of the string W).
- One line with the text T, a string over {'A', 'B', 'C', …, 'Z'}, with |W| ≤ |T| ≤ 1,000,000.
Output
For every test case in the input file, the output should contain a single number, on a single line: the number of occurrences of the word W in the text T.
Sample Input
3
BAPC
BAPC
AZA
AZAZAZA
VERDI
AVERDXIVYERDIAN
Sample Output
1
3
0
kmp统计子串出现的次数,注意当p1>t_len的时候,如果s串中没有空字符,那么我们就相当于考虑,(注意Next数组算到maxIndex+1
T的最后一个空字符和S不匹配,那么说明T之前的字符和S都匹配了,直接走p1=Next[p1]是正确的,直接p1=1是错的,会少算答案
比如AZAZAZA,AZA,当第二个串跑到4的时候用Next可以跳到2,可以继续匹配中间的AZA,而直接回溯一,就只能直接算最后一个AZA
#include <iostream>
#include <cstdio>
#include <cstring>
using namespace std;
const int maxn=1e6+7;
char S[maxn],T[maxn];
int Next[maxn];
int main(){
int t;scanf("%d",&t);
while(t--){
scanf("%s%s",T+1,S+1);
memset(Next,-1,sizeof(Next));
int s_len=strlen(S+1),t_len=strlen(T+1);
Next[1]=0;int p1=0,p2=1;
while(p2<=t_len){
if(p1==0){
Next[++p2]=1;p1=1;
}
else if(T[p1]==T[p2]){
Next[++p2]=++p1;
}
else p1=Next[p1];
}
int ans=0;
p1=p2=1;
while(p2<=s_len){
if(p1==0||T[p1]==S[p2]){
p1++;p2++;
if(p1>t_len){
ans++;//这里不用回溯p1指针
}
}
else {
p1=Next[p1];
}
}
printf("%d\n",ans);
}
return 0;
}
Cyclic Nacklace
Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others)
Total Submission(s): 8622 Accepted Submission(s): 3707
As Christmas is around the corner, Boys are busy in choosing christmas presents to send to their girlfriends. It is believed that chain bracelet is a good choice. However, Things are not always so simple, as is known to everyone, girl's fond of the colorful decoration to make bracelet appears vivid and lively, meanwhile they want to display their mature side as college students. after CC understands the girls demands, he intends to sell the chain bracelet called CharmBracelet. The CharmBracelet is made up with colorful pearls to show girls' lively, and the most important thing is that it must be connected by a cyclic chain which means the color of pearls are cyclic connected from the left to right. And the cyclic count must be more than one. If you connect the leftmost pearl and the rightmost pearl of such chain, you can make a CharmBracelet. Just like the pictrue below, this CharmBracelet's cycle is 9 and its cyclic count is 2:
Now CC has brought in some ordinary bracelet chains, he wants to buy minimum number of pearls to make CharmBracelets so that he can save more money. but when remaking the bracelet, he can only add color pearls to the left end and right end of the chain, that is to say, adding to the middle is forbidden.
CC is satisfied with his ideas and ask you for help.
Each test case contains only one line describe the original ordinary chain to be remade. Each character in the string stands for one pearl and there are 26 kinds of pearls being described by 'a' ~'z' characters. The length of the string Len: ( 3 <= Len <= 100000 ).
aaa
abca
abcde
2
5
注意输出答案时对于Next[len]=1的特判
abca,abce的区分
以及xyzabcabcqe这个是无周期的
只要前面出现了后缀,后面出现了前缀,那么我们可以只补右面,直接从左往右找周期串,不必把中间的周期串抠出来
比如exyzabcabcqe,qexyzabcabcqe,qexyzabcabcqex,xyzabcabcqex,xyzabcabcqexy等
以及周期串的长度一定是len-Next[len],注意len-Next[len]=1的特判和Next[len]=1的特判
#include <iostream>
#include <cstdio>
#include <cstring>
using namespace std;
const int maxn=1e5+7;
char s[maxn];int Next[maxn];
int main(){
int T;scanf("%d",&T);
while(T--){
scanf("%s",s+1);
memset(Next,-1,sizeof(Next));
Next[1]=0;int p1=0,p2=1,len=strlen(s+1);
while(p2<=len){
if(p1==0){
Next[++p2]=1;p1=1;
}
else if(s[p1]==s[p2]){
Next[++p2]=++p1;
}
else p1=Next[p1];
}
int mod=len-Next[len];
if(Next[len]==1){
if(s[len]==s[1]) printf("%d\n",len-2);//周期串长度本身变成len-1,在减去已经有的结尾,len-1-1
else printf("%d\n",len);
continue;
}
if(mod==1){
if(s[len]==s[Next[len]]) printf("0\n");
else printf("%d\n",len);
}
else{
if(len%mod==0) printf("0\n");
else printf("%d\n",mod-len%mod);
}
}
return 0;
}
对于kmp求next数组的理解的更多相关文章
- KMP中next数组的理解
next数组是KMP的核心,但对于next数组我们总是有时候感觉明白了,但有时候又感觉没明白,现在我就说下我自己对KMP中next数组的理解,首先next[i]上的数字的意义,next[i]表示的是当 ...
- POJ 2752 KMP中next数组的理解
感觉这里讲的挺好的.http://cavenkaka.iteye.com/blog/1569062 就是不断递归next数组.长度不断减小. 题意:给你一个串,如果这个串存在一个长度为n的前缀串,和长 ...
- KMP中next数组的理解与应用
理解 1.next数组一直往前走 next数组一直往前走,得到的所有前缀也是当前主串的后缀,当然了,也是当前主串的前缀. 2.周期性字符串 1.周期性字符串$\Leftrightarrow n \,\ ...
- KMP 求next数组
一直没理解.看这个倒是看懂了.但是博主代码好像有点问题吖.测试并不正确.思想还是没错的. 转载自:http://www.tuicool.com/articles/yayeIbe
- poj 2406:Power Strings(KMP算法,next[]数组的理解)
Power Strings Time Limit: 3000MS Memory Limit: 65536K Total Submissions: 30069 Accepted: 12553 D ...
- 【KMP求最小周期】POJ2406-Power Strings
[题意] 给出一个字符串,求出最小周期. [思路] 对KMP的next数组的理解与运用orz ①证明:如果最小周期不等于它本身,则前缀和后缀必定有交叉. 如果没有交叉,以当前的next[n]为最小周期 ...
- 【bzoj2384】[Ceoi2011]Match 特殊匹配条件的KMP+树状数组
题目描述 给出两个长度分别为n.m的序列A.B,求出B的所有长度为n的连续子序列(子串),满足:序列中第i小的数在序列的Ai位置. 输入 第一行包含两个整数n, m (2≤n≤m≤1000000). ...
- 求最长公共前缀和后缀—基于KMP的next数组
KMP算法最主要的就是计算next[]算法,但是我们知道next[]求的是当前字符串之前的子字符串的最大前后缀数,但是有的时候我们需要比较字符串中前后缀最大数,比如 LeetCode的shortest ...
- KMP算法中我对获取next数组的理解
之前在学KMP算法时一直理解不了获取next数组的函数是如何实现的,现在大概知道怎么一回事了,记录一下我对获取next数组的理解. KMP算法实现的原理就不再赘述了,先上KMP代码: 1 void g ...
随机推荐
- ClickHouse入门:表引擎-HDFS
前言插件及服务器版本服务器:ubuntu 16.04Hadoop:2.6ClickHouse:20.9.3.45 文章目录 简介 引擎配置 HDFS表引擎的两种使用形式 引用 简介 ClickHous ...
- 关于springboot2.X使用外部tomcat服务器进行部署的操作详细步骤
1.修改pom.xml文件(4个地方) ①<packaging>war</packaging>将其中的jar该为war ②<dependency> <grou ...
- 学习es6构造函数的第一天
什么是面向对象 编程思维分为,面向过程和面向对象 面向过程就像一个人,一间屋子,一个床 一个人走进了屋子,上了床 二面向对象 人,屋子,床 可以是屋子里进了一个人,上了床 或者,屋子里的床上有一个人 ...
- 览器全面禁用三方 Cookie 全栈前端精选 4月16日
览器全面禁用三方 Cookie 全栈前端精选 4月16日
- 奇艺iOS移动端网络优化实践 | 请求成功率优化篇 原创 Charles 爱奇艺技术
奇艺iOS移动端网络优化实践 | 请求成功率优化篇 原创 Charles 爱奇艺技术
- functools.singledispatchmethod(Python 3.8) | 码农网 https://www.codercto.com/a/83245.html
functools.singledispatchmethod(Python 3.8) | 码农网 https://www.codercto.com/a/83245.html
- CSS补充2
浮动是css里面布局最多的一个属性效果:两个元素并排了,并且两个元素都能够设置宽度和高度 四个特性: 1.浮动的元素脱标 2.浮动的元素互相贴靠 3.浮动的元素有"字围"效果 4. ...
- MySQL 数据库性能调优
MySQL 数据库性能调优 MySQL性能 最大数据量 最大并发数 优化的范围有哪些 存储.主机和操作系统方面: 应用程序方面: 数据库优化方面: 优化维度 数据库优化维度有四个: 优化选择: 数据库 ...
- 开发基础 (变量、数据类型、格式化输出、运算符、流程控制、while循环)
一.变量 name = "SmallNine" 等号前面是变量名(标识符),等号后面是变量值 变量的主要作用:就是把程序运算的中间结果临时存到内存里,已备后面的代码继续调用. 变量 ...
- WPF 之 MultiBinding(多路 Binding)(四)
一.前言 有时候 UI 需要显示的信息由不止一个数据来源决定,这时候就需要使用 MultiBinding ,即多路 Binding. MultiBinding 与 Binding 一样均以 B ...