对于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 ...
随机推荐
- GlusterFS分布式存储系统复制集更换故障Brick操作记录
场景: GlusterFS 3节点的复制集,由于磁盘故障,其中一个复制集需要重装系统,所以需要重装glusterfs并将该节点加入glusterfs集群 一. 安装GlusterFS 首先在重装系统节 ...
- jmeter报Address already in use: connect
jmeter报Address already in use: connect 用windows进行jmeter压测出现java.net.BindException: Address already ...
- 【Soul源码探秘】插件链实现
引言 插件是 Soul 的灵魂. Soul 使用了插件化设计思想,实现了插件的热插拔,且极易扩展.内置丰富的插件支持,鉴权,限流,熔断,防火墙等等. Soul 是如何实现插件化设计的呢? 一切还得从插 ...
- Java并发组件一之CountDownLatch
使用场景: 一个或N个线程,等待其它线程完成某项操作之后才能继续往下执行.CountDownLatch描述的是,一个或N个线程等待其他线程的关系. 使用方法: 设CountDownLatch个数:Co ...
- using-pointers-to-remove-item-from-singly-linked-list
https://stackoverflow.com/questions/12914917/using-pointers-to-remove-item-from-singly-linked-list
- Java NIO ———— Buffer 缓冲区详解 入门
引言缓冲区是一个用于特定基本类型的容器.由java.nio 包定义,所有缓冲区都是 Buffer 抽象类的子类. Java NIO 中的 Buffer ,主要用于与NIO 通道进行交互.数据从通道存入 ...
- Redis击穿、穿透、雪崩产生原因以及解决思路
击穿 大家都知道,计算机的瓶颈之一就是IO,为了解决内存与磁盘速度不匹配的问题,产生了缓存,将一些热点数据放在内存中,随用随取,降低连接到数据库的请求链接,避免数据库挂掉.需要注意的是,无论是击穿还是 ...
- Cisco的互联网络操作系统IOS和安全设备管理器SDM__备份和恢复Cisco 配置
对路由器配置进行的任何修改存储在running-config文件中.如果在修改了running-config后没有输入copy run start命令,那么路由器重载或掉电后,修改的内容会丢失. 1. ...
- Spring 事务、异步和循环依赖有什么关系?
前言 在循环依赖中有一种循环依赖,就是自注入:自己依赖自己. 事务的自注入 在 Spring 自调用事务失效,你是怎么解决的? 有小伙伴提出可以自己注入自己来解决事务失效. 具体使用方式如下: @Sl ...
- docker(8)Dockerfile指令介绍
前言 Dockerfile 是一个用来构建镜像的文本文件,文本内容包含了一条条构建镜像所需的指令和说明. Dockerfile简介 Dockerfile是用来构建Docker镜像的构建文件,是由一系列 ...