HDU 3746 Cyclic Nacklace(KMP循环节问题)

题意:

给出一个串,可以在左端或右端加字符。问使这个串至少有两个循环节,至少要加多少个字符。其实在左和右加是一样的,这里我们讨论在右加。

关于KMP循环节问题:

首先可以确定一个事情,就是一个字符串,它可以分为两类。

1.不完整的循环串
2.完整的循环串
比如abcabcabc就是一个有三个循环节的完整的循环串,而abcabca就是有两个循环节的不完整的循环串,它要加上ab才能成为有三个循环节的完整的循环串abcabcabc
同理特殊的abcd是一个有一个循环节的串,要补充为abcdabcd才能成为两个循环节的串。
下面来定理:
不论是不是完整的循环串,字符串s的长度为len,则s的最小循环节长度为len-next[len]。注意用的是字符串长度的next[len].
1.若满足len%(len-next[len])=0,该串是完整的循环串,且有len/(len-next[len])个循环节。
证明:最小循环节的意思就是该字符串是由这个最小循环节重复而形成的,能够整除则说明字符串有整数个这样的最小循环节,那么就是完整的循环串,也就是字符串是完整的循环串。
2.如果不能整除则说明不是完整的循环串,余数为x,则要将字符串变成完整的循环串,需要加len-next[len]-len%(len-next[len])个字符。
这个比较好证明,余数就是最后那个不完整的循环节的长度,让它变成一个循环节就能使循环串成为完整的循环串。

len-next[len]的图解说明:

1.next数组的长度没超过字符串一半,也就是相同前缀后缀没超过一半。
在这里插入图片描述
其实就相当于abcda的形式,只有一个半循环节,最小循环节长度就是去掉next数组的长度。
2.next长度超过一半
在这里插入图片描述
红色部分如果匹配的话,其实把字符串分为如上三部分,其实B就是A的一个前缀,正好是两个半循环节组成,循环节长度就是绿色的长度也就是A的长度,就是len-next[len]。
其实还可以是多个最小循环节,也是同理,就是字符串分的部分多了而已,就是多个半循环节
在这里插入图片描述
3.如果只有一个循环节,那么一定next[len]=0,因为如果next[len]不等于0一定可以归为上面两类。

思路:

有了上面的结论,那么这个题就好做了。
1.首先判断是不是该串本身就是含有两个以上循环节的完整循环节。排除只有一个循环节的情况,并且整除:
next[len]>0&&len%(len-next[len])=0
2.不是上面的情况就只能补了,先求出尾巴(就是那半个循环节)的长度len%(len-next[len])。再用循环节长度减去它即可。

代码:

#include<cstdio>
#include<cstring>
#include<string>
#include<iostream>
using namespace std;
typedef long long ll;
const int maxn=1e5+100;
char p[maxn];
int nxt[maxn];
void getnxt(int plen)
{
    nxt[0]=-1;
    int j=0,k=-1;
    while(j<plen)
    {
        if(k==-1||p[j]==p[k])
            nxt[++j]=++k;
        else k=nxt[k];
    }
}
int main()
{
    int t;
    scanf("%d",&t);
    while(t--)
    {
        scanf("%s",p);
        int plen=strlen(p);
        getnxt(plen);
        int len1=plen-nxt[plen];
        //if(nxt[plen]==0) printf("%d\n",plen);
        if(nxt[plen]>0&&plen%len1==0) printf("0\n");
        else printf("%d\n",len1-plen%len1);
    }
    return 0;
}

猜你喜欢

转载自blog.csdn.net/qq_44607936/article/details/99691807
今日推荐