对于初学 KMP next数组大同小异,有的时候学的下标1的next数组在做题很困难。
其原因还是不熟练,因为两者的本质是一样的。
next数组的作用:
子串在与主串匹配过程中,当子串的第 j 个位置与主串的第 i 个位置不匹配时,子串前移(子串实际上不移动,所以用到具有向量作用的next数组),使子串的第next[ j ]与主串的第 i 个位置比较。
(以下的前缀和后缀指的是真前缀和真后缀)
下标从0开始的 next 数组
next[ i ] 表示前面长度为 i 的字符串前缀和后缀相等的最大长度为 next[ i ] 。
下标从1开始的 next数组
next[ i ] 表示前面长度 i - 1的字符串中前缀和后缀相等的最大长度为 next[ i ] - 1。
所以在做一些 :子串在母串中出现了几次、最小循环节出现个数下标为0的next数组较为方便。(一开始学的下标为1 的,然后也不知道本质是什么,学kmp快一个星期了吧,直到现在才转过来弯)。
kmp模板下标为0:
//求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;
}
因为我学的下标为1,其实也能写,不过找到一个特别详尽的,几乎都概括了,自认不如,所以转载了一下。
出处:https://www.cnblogs.com/chenxiwenruo/p/3546457.html
不愿意看这个模板看出处也挺好,而且还包含了最短循环节周期的证明,真的挺不错。
下标为1:
#include <iostream>
#include <cstring>
#include <cstdio>
using namespace std;
char ss[1000],s[666];
int Next[666];
void getNext(char *s){
int j = 1, t = 0;
int len = strlen(s)+1;
Next[1] = 0;
while(j < len){
if(t == 0 || s[j] == s[t]){
Next[j+1] = t+1;
j++; t++;
}
else t = Next[t];
}
}
int KMP(char *ss, char *s){ //作用,子字符串第一次出现的位置.下标从1开始.
int i = 1, j = 1;
int len1 = strlen(ss+1), len2 = strlen(s+1);
while(i <= len1 && j <= len2){
if(j == 0 || ss[i] == s[j]){
j++; i++;
}
else j = Next[j];
}
if(j > len2) return i - len2;
else return 0;
}
int main(){
scanf("%s",ss+1);
scanf("%s",s+1);
getNext(s);
cout << KMP(ss,s) << endl;
return 0;
}
写的有点寒酸,我就测试了一遍就写这来的,不过应该对!