看了好久的kmp终于懂了,做几个题目热热身


问题 L: 【KMP】Radio Transmission

时间限制: 1 Sec  内存限制: 128 MB
提交: 69  解决: 27
[提交] [状态] [命题人:admin]

 

题目描述

给你一个字符串,它是由某个字符串不断自我连接形成的。但是这个字符串是不确定的,现在只想知道它的最短长度是多少。

 

输入

第一行给出字符串的长度L,第二行给出一个字符串,全由小写字母组成。

 

输出

输出最短的长度。

 

样例输入

复制样例数据

8
cabcabca

样例输出

3

 

提示

我们可以利用abc不断自我连接得到abcabcabc,读入的cabcabca是它的子串。

对于全部数据,1≤L≤106


题解思路:考虑next数组的性质 ,分以下三种情况:

abab  next[n]=2 因为 由两个ab组成

ababa next[n]=3 因为由两个 aba组成

abababa next[n]=5  因为由两个ababa组成

所以说 这个字符串如果最后的循环节没有完成 :比如ababa 他就会使本来的 ab前后缀 加一变成aba前后缀,所以我们这样考虑:每在最小循环节上复制一次 他的最长公共前后缀就会增加他复制的长度,把最小循环节空出来,所以我们可以得到,最小正周期

T=n-next[n];

AC:

//#include <bits/stdc++.h>
#include<stdio.h>
#include <string.h>
#include<algorithm>
#pragma GCC optimize(2)
typedef long long ll;
typedef unsigned long long ull;
const int mod=998244353;
const int maxn=1e6+5;
const ll INF=10000000000000005;
using namespace std;
ll n,m;
inline int read()
{
    int x=0,f=1;char ch=getchar();
    while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
    while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
    return x*f;
}
char s[maxn];
int f[maxn];
void restart()
{
    f[0]=-1;
    int i=-1,j=0;
    while(j<n)
    {
        if(i==-1||s[j]==s[i])
        {
            i++;
            j++;
            f[j]=i;
        }
        else
            i=f[i];
    }
}
int main()
{
    scanf("%lld%s",&n,s);
    restart();
    bool fl=false;
    printf("%lld\n",n-f[n]);
    return 0;
}

总结:根据这个题我们还能缩小我们之前求最小正周期的算法(二层循环),直接预处理next数组,n-f[n]即为最小正周期


问题 M: 【KMP】OKR-Periods of Words

时间限制: 1 Sec  内存限制: 128 MB
提交: 40  解决: 16
[提交] [状态] [命题人:admin]

 

题目描述

串是有限个小写字符的序列,特别的,一个空序列也可以是一个串。一个串P是串A的前缀,当且仅当存在串B,使得A=PB。如果P≠A并且P不是一个空串,那么我们说P是A的一个proper前缀。
定义Q是A的周期,当且仅当Q是A的一个proper前缀并且A是QQ的前缀(不一定要是proper前缀)。比如串abab和ababab都是串abababa的周期。串A的最大周期就是它最长的一个周期或者是一个空串(当A没有周期的时候),比如说,ababab的最大周期是abab。串abc的最大周期是空串。
给出一个串,求出它所有前缀的最大周期长度之和。

 

输入

第一行一个整数k,表示串的长度。
接下来一行表示给出的串。

 

输出

输出一个整数表示它所有前缀的最大周期长度之和。

 

样例输入

复制样例数据

8
babababa

样例输出

24

 

提示

对于全部数据,1<k<106


题解:上个题我们求的最小正周期,这个题让我们求最大正周期,我们知道next数组求的是最大公共前后缀,所以n-f[n]绝对是最小正周期,那最大正周期呐?我们运用next数组的性质,不断判断这个位置i的最小公共前后缀是多少 只要next[next[i]]>0 说明 next[i]=next[next[i]](只要!=0||!=-1说明还有最大公共前后缀我们不停的缩小),最后i-next[i]结果,过程中next[i]可以快速更新:

AC:

//#include <bits/stdc++.h>
#include<stdio.h>
#include <string.h>
#include<algorithm>
#pragma GCC optimize(2)
typedef long long ll;
typedef unsigned long long ull;
const int mod=998244353;
const int maxn=1e6+5;
const ll INF=10000000000000005;
using namespace std;
ll n,m;
inline int read()
{
    int x=0,f=1;char ch=getchar();
    while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
    while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
    return x*f;
}
char s[maxn];
int f[maxn];
void restart()
{
    f[0]=-1;
    int i=-1,j=0;
    while(j<n)
    {
        if(i==-1||s[j]==s[i])
        {
            i++;
            j++;
            f[j]=i;
        }
        else
            i=f[i];
    }
   /* for(int i=0;i<=n;i++)
        printf("%d ",f[i]);*/
}
int main()
{
    scanf("%lld%s",&n,s);
    restart();
    bool fl=false;
    ll ans=0;
    for(int i=1;i<=n;i++)
    {
        while(f[f[i]]>0) f[i]=f[f[i]];
        printf("%d ",f[i]);
        if(f[i]!=0) ans+=i-f[i];
    }
    printf("%lld\n",ans);
    return 0;
}

总结:掌握了求最小公共前后缀的办法,原理用例子解释一下:ababa next[5]=3 next[3]=1 next[1]=0 所以next[5]=1 最小