KMP模板 + next下标从0和1所代表的含义

对于初学 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 
#include 
#include 

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;
}

写的有点寒酸,我就测试了一遍就写这来的,不过应该对!

你可能感兴趣的:(KMP)