CodeForces961F- k-substrings(哈希)

Description:
You are given a string s consisting of n lowercase Latin letters.
Let’s denote k-substring of s as a string subsk = sksk + 1…sn + 1 - k. Obviously, subs1 = s, and there are exactly such substrings.
Let’s call some string t an odd proper suprefix of a string T iff the following conditions are met:
·|T| > |t|;
·|t| is an odd number;
·t is simultaneously a prefix and a suffix of T.
For evey k-substring () of s you have to calculate the maximum length of its odd proper suprefix.

Input:
The first line contains one integer n (2 ≤ n ≤ 106) — the length s.
The second line contains the string s consisting of n lowercase Latin letters.

Output:
Print integers. i-th of them should be equal to maximum length of an odd proper suprefix of i-substring of s (or  - 1, if there is no such string that is an odd proper suprefix of i-substring).

Sample Input:
15
bcabcabcabcabca
Sample Output:
9 7 5 3 1 -1 -1 -1

Sample Input:
24
abaaabaaaabaaabaaaabaaab
Sample Output:
15 13 11 9 7 5 3 1 1 -1 -1 1

Sample Input:
19
cabcabbcabcabbcabca
Sample Output:
5 3 1 -1 -1 1 1 -1 -1 -1

Note:
The answer for first sample test is folowing:
·1-substring: bcabcabcabcabca
·2-substring: cabcabcabcabc
·3-substring: abcabcabcab
·4-substring: bcabcabca
·5-substring: cabcabc
·6-substring: abcab
·7-substring: bca
·8-substring: c

题意:
给出一个长度为n的字符串s,输出(n+1)/2个数,每个数代表从字符串第k位开始到第n+k-1位的序列中,满足既是其前缀又是其后缀的子串的最大奇数长度,如果不存在就输出-1(注意本题要求前后缀不能重叠)。

解法:
仿佛一道玄学题。总而言之,我们的时间复杂度是O(n)。
首先我们要发现一个结论,ans[i]<=ans[i+1]+2,因为两个序列的长度差是2(别问我为什么要发现这个,我也没发现) 。
考虑倒着计算ans[i](也就是从中间开始向左右延伸),左右端点设置为l和r,前缀长度从ans[i+1]+2枚举到1,用hash判断是否相等,相等就保存答案,如果循环结束了ans[i]还是0,意味着不存在该前缀,那就把答案设置成-1。最后一并输出答案。
方法来源于https://www.luogu.com.cn/problemnew/solution/CF961F。

代码:

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<stdlib.h>
#include<cstring>
using namespace std;

int n,l,r;
int ans[1000000+5];
long long mmod=2333,mod=1e9+7;
long long shash[1000000+5],base[1000000+5];
char s[1000000+5];

int main()
{
    scanf("%d%s",&n,s+1);
    base[0]=1;
    for(int i=1;i<=n;i++){
        shash[i]=(shash[i-1]*mmod+s[i]-'a'+1)%mod;
        base[i]=base[i-1]*mmod%mod;
    }
    if(n%2==1){
        ans[(n+1)/2]=-1;l=r=(n+1)/2;
    }else{
        l=n/2;r=n/2+1;
        if(s[l]==s[r])ans[n/2]=1;
        else ans[n/2]=-1;
    }
    for(int i=(n+1)/2-1;i>=1;i--){
        l--;r++;
        for(int j=ans[i+1]+2;j>=1;j-=2){
            long long a=(shash[l+j-1]-shash[l-1]*base[j]%mod+mod)%mod;
            long long b=(shash[r]-shash[r-j]*base[j]%mod+mod)%mod;
            if(a==b){
                ans[i]=j;
                break;
            }
        }
        if(ans[i]==0)ans[i]=-1;
    }
    for(int i=1;i<(n+1)/2;i++)printf("%d ",ans[i]);
    printf("%d",ans[(n+1)/2]);
    return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值