KMP【模板】

来源:互联网 发布:驱鼠器真的有用吗 知乎 编辑:程序博客网 时间:2024/05/02 00:49

当字符串匹配失败时,模式串的指针并没有指向0从头比较,而是指向了一个特定的位置,因为这个Next[j]指向的位置pos前长度为Next[pos]的子串,同模式串第j位前的长度为Next[j]的子串是相同的。
即S[0]~S[Next[j]]一定与S[len-1-Next[j]]~S[j-1]匹配。
1.既能做前缀又能做后缀的子串长度

 ans[0] = len;          int id = 1,i = len;          while(Next[i] > 0)          {              ans[id++] = Next[i];              i = Next[i];          }  

2.求最短子串长度
给定的字符串s,最短的重复子串a是s[Next[len]] s[Next[len+1]] … s[len-1]。
当len % Next[len] = 0时,说明字符串s有不是它本身重复子串a,最大的n为len/Next[len]。当len % Next[len] != 0时,除了s,没有重复子串a满足要求,这时n = 1。

if(len % (len-Next[len]) == 0)              cout << len/(len-Next[len]) << endl;          else              cout << 1 << endl;  

3.求模式串pat在主串str中匹配次数
可重叠匹配:当一次匹配成功后,j继续回退到Next[j]向后进行匹配,直到主串str的末尾。
不可重叠匹配:当一次匹配成功后,j继续回退到0向后进行匹配,直到主串str的末尾。
4.求N行M列的字符矩阵,最小的字符子矩阵的面积
每一行s[i]来说,M-Next[M]是最小的,对于每一列s[j]来说,N-Next[N]是最小的。
分别求每一行的最小公倍数lcmn和每一列的最小公倍数lcmm。最后结果就是lcmn*lcmm。
5.求字符串s的循环前缀的长度和循环的次数
对于当前前缀S[1]~S[i],如果i % (i-Next[i]) == 0,则i - Next[i]就是最小重复子串,即循环节长度为i - Next[i]。循环次数为i / (i - Next[i])。

char str[1000010],pat[1000010];//pat为模式串,str为主串int Next[1000010]; //Next[x]下标x表示匹配失败处字符下标//模式串pat的前缀与x位置的后缀的最大匹配字符个数-1void GetNext(char *pat){    int LenPat = strlen(pat);    int i = 0,j = -1;    Next[0] = -1;    while(i < LenPat)    {        if(j == -1 || pat[i] == pat[j])        {            i++,j++;            Next[i] = j;        }        else            j = Next[j];    }}int KMP()//返回模式串pat在str中第一次出现的位置{    int LenStr = strlen(str);    int LenPat = strlen(pat);    GetNext(pat);    int i = 0,j = 0;    int ans = 0;//计算模式串在主串匹配次数    while(i < LenStr)    {        if(j == -1 || str[i] == pat[j])            i++,j++;        else            j = Next[j];        if(j == LenPat)        {            //ans++; ans存放匹配次数,去掉return,最后返回ans            return i - LenPat + 1;        }    }    return -1;//没找到匹配位置    //return ans;//返回匹配次数。}
0 0
原创粉丝点击