KMP深入理解——next数组求最大周期,最小循环节

next数组求最大周期
P3435 [POI2006] OKR-Periods of Words

题意:
给定一个字符串,请你求出这个字符串的所有前缀的最大周期和。

周期:一个串s的前缀a重复两次能够令s成为aa的子串称a为s的周期。

思路:我们利用kmp的next数组来求解,

next数组的含义:next[i]代表字符串的第i个前缀a的既是其前缀又是其后缀的串的长度。

next的用意:我们通过next数组能够避免从头开始寻找目前串的匹配,可以直接寻找目前满足这个子串最长既是其前缀又是其后缀的串。

对于这个题,我们通过next数组可以直接不断递推出当前串的循环节。

我们对于第i个前缀,先令j=i,不断的令j=nex[j],直到当前的nex[j]指示为空集,然后就能得到当前前缀的最小循环节为i-j。

对于这个循环节来讲,整个第i个前缀都是其appear length

#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
const int N = 1e6+100;
int n,len2;
int nex[N];
char s2[N];
inline void get_nex() //求出nex数组
{ //nex数组是从 S[0到i-1]前子串 的前缀后缀最大值
    int t1=0,t2;
    nex[0]=t2=-1;
    while(t1<len2)
        if(t2==-1 || s2[t1]==s2[t2]) //类似于KMP的匹配
            nex[++t1]=++t2;
        else t2=nex[t2];//失配
}

int main()
{
    cin>>len2;
    scanf("%s",s2);
    get_nex();
    long long ans=0ll;
    for(int i=1;i<=len2;i++)
    {
        int j=i;
        while(nex[j])
            j=nex[j];
        if(nex[i])
            nex[i]=j;//记忆化
        ans+=i-j;
    }
    cout<<ans;
    return 0;
}

J. MUV LUV EXTRA

题意:给定一个字符串和两个常数a,b。请你求出其后缀的所有最小循环节长度l,并且计算ap-bl的最小值

思路:暴力求解必然会超时。我们想一下怎么利用kmp的next数组求解

next数组的含义上一题也说过了。

最小循环节能看出来就是:s的长度减去s的最长的既是前缀又是后缀的子串的长度。

比如:ababa 他的最长公共前后缀长度为3是aba,那么他的最小循环节应该就是ab,可以视为把s末尾的aba去掉了。因为aba是公共前后缀,所以去掉后一样可以通过ab重复多次来获得。

注意:题目的式子可能有负数,所以初始答案值设为无穷小。

#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
#define int long long
const int N = 1e7+100;
int n,nex[N],a,b;
char s[N];
void get_next()
{
    for(int i=2,j=0;i<=n;i++)
    {
        while(j&&s[i]!=s[j+1])
            j=nex[j];
        if(s[j+1]==s[i])
            j++;
        nex[i]=j;
    }
}
signed main()
{
    cin.tie(0);cout.tie(0);ios::sync_with_stdio(0);
    cin>>a>>b;
    string t;
    cin>>t;
    for(int i=t.length()-1;i>=0;i--)
    {
        if(t[i]=='.')
            break;
        s[++n]=t[i];
    }
    get_next();
    int ans=-1e18;
    for(int i=1;i<=n;i++)
    {
        ans=max(ans,a*i-b*(i-nex[i]));
    }
    cout<<ans;
    return 0;
}

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值