2019ccpc秦皇岛J MUV LUV EXTRA

http://acm.hdu.edu.cn/contests/contest_showproblem.php?cid=872&pid=1010
题意可以转化为:给你一个字符串 S S S O ( n ) O(n) O(n)求每一个后缀的最短循环节(最后几位可以不完整,比如1234123412的最短循环节是1234,最后的12不完整但和循环节前2个匹配)。
思路:
其实和kmp求完整循环节差不多。
先把字符串反转一下,就转化为了求每一个前缀的最短循环节。
i − n e x t ( i ) i-next(i) inext(i)就是前 i i i个字符的最短循环节,为什么呢?比如原来是1234123412,反转后变成2143214321,我们的本意是 [ 21 ] [ 4321 ] [ 4321 ] [21][4321][4321] [21][4321][4321],但是,分组偏移一下是一样的!即 [ 2143 ] [ 2143 ] [ 21 ] [2143][2143][21] [2143][2143][21]
而对于前缀找循环节,简单假证一下,令 k = = i − n e x t ( i ) k==i-next(i) k==inext(i),那么 s ( i ) = = s ( i + k ) s(i)==s(i+k) s(i)==s(i+k),这样,把字符串s划分为了 k k k个等价类,取相邻的 k k k个为一组,那么 i / k i/k i/k就是完整的循环节个数, i % k i\%k i%k是不完整但匹配的部分长度。
代码如下,注意ans初始化为-INF,而不是0。

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn=10000000+100;

ll a,b,ans;
int f[maxn];
char s[maxn];

int main()
{
    //freopen("input.in","r",stdin);
    while(cin>>a>>b)
    {
        ans=-(1LL<<60);
        char c=0;
        while(c!='.')c=getchar();
        scanf("%s",s);
        int len=strlen(s);
        for(int i=0;i<len/2;i++)swap(s[i],s[len-1-i]);
        for(int i=1;i<len;i++)
        {
            int j=f[i];
            while(j && s[i]!=s[j])j=f[j];
            f[i+1]=(s[i]==s[j]?j+1:0);
        }
        for(int i=len;i>=1;i--)
        {
            ans=max(ans,a*i-b*(i-f[i]));
        }
        cout<<ans<<endl;
    }
    return 0;
}
评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值