KMP找最小循环节

KMP找最小循环节

 

一:kmp模板:其中next【0】=0

#define KMP_GO(X) while(k>0 && P[k]!=X[i]) k=next[k];if(P[k]==X[i])k++

//求字符串P在T中出现的次数

int kmp_match(char*T,char*P){

    int n,m,next[10010],i,k,c;

    n=strlen(T);m=strlen(P);

    next[1]=k=0;

    for(i=1;i

        KMP_GO(P);

        next[i+1]=k;//这里i表示的是字符的索引,对应的长度i+1

    }

    k=c=0;

    for(i=0;i

        KMP_GO(T);

        if(k==m){

            c++;

            k=next[k];

        }

    }

    return c;

}

 

 

二:KMP最小循环节、循环周期:

    定理:假设S的长度为len,则S存在最小循环节,循环节的长度L为len-next[len],子串为S[0…len-next[len]-1]。

(1)如果len可以被len - next[len]整除,则表明字符串S可以完全由循环节循环组成,循环周期T=len/L。

(2)如果不能,说明还需要再添加几个字母才能补全。需要补的个数是循环个数G=L-len%L=L-(len-L)%L=L-next[len]%L,其中L=len-next[len]。

 

 

理解该定理,首先要理解next数组的含义:next[i]表示前面长度为i的子串中,前缀和后缀相等的最大长度。

 

 

接下来举几个例子来说明最小循环节和循环周期:

为方便说明,先设字符串的长度为len,循环子串的长度为L

1.

s0s1s2s3s4s5 ,next[6]=3

即s0s1s2=s3s4s5

很明显可知:循环子串为s0s1s2,L=len-next[6]=3,且能被len整除。

 

2.

s0s1s2s3s4s5s6s7 ,next[8]=6

此时len-next[8]=2 ,即L=2

由s0s1s2s3s4s5=s2s3s4s5s6s7

可知s0s1=s2s3,s2s3=s4s5,s4s5=s6s7

显然s0s1为循环子串

 

3.

s0s1s2s3s4s5s6 ,next[7]=4

此时len-next[7]=3,即L=3

由s0s1s2s3=s3s4s5s6

可知s0s1=s3s4,s2s3=s5s6

从而可知s0s1s2=s3s4s5,s0=s3=s6

即如果再添加3-4%3=2个字母(s1s2),那么得到的字符串就可以由s0s1s2循环3次组成

 

 

模板题hdu3746

 

题意:  

给你一个字符串,让你在后面加尽量少的字符,使得这个字符串成为一个重复串。

例:

abca---添加bc,成为abcabc

abcd---添加abcd,成为abcdabcd

aa---无需添加

 

分析:

经典的求最小循环节,找到最小循环节长度:len-next[len]。然后用总长度%最小循环节长度得到答案

#include

#define  LL long long

#define  ULL unsigned long long

using namespace std;



const int MAXN=100010;

char s[MAXN];

int Next[MAXN];

void getNext()

{

      Next[0]=0;

      int s_len=strlen(s);

      for(int i=1,k=0;i


 

参考文献

https://www.cnblogs.com/chenxiwenruo/p/3546457.html

你可能感兴趣的:(字符串,最小循环节)