字符串(づ。◕‿‿◕。)づ进阶之章

字符串(づ。◕‿‿◕。)づ进阶之章

一、[国家集训队]最长双回文串

题目描述

顺序和逆序读起来完全一样的串叫做回文串。比如acbca是回文串,而abc不是(abc的顺序为abc,逆序为cba,不相同)。

输入长度为n的串S,求S的最长双回文子串T,即可将T分为两部分XY,(|X|,|Y|≥1∣X∣,∣Y∣≥1)且XY都是回文串。

输入格式

一行由小写英文字母组成的字符串S

输出格式

一行一个整数,表示最长双回文子串的长度。

输入输出样例

输入 #1

baacaabbacabb

输出 #1

12

说明/提示

【样例说明】

从第二个字符开始的字符串aacaabbacabb可分为aacaabbacabb两部分,且两者都是回文串。

对于100%的数据,2≤∣S∣≤10^5

2018.12.10、2018.12.15 感谢 @Ycrpro 提供 hack 数据两组

先马拉车算法,然后记录左右边界这个时候记录的只有最大值的左右边界,然后再递推再更新醉的值,最后遍历一遍求出最大值即可

AC code

#include <bits/stdc++.h>
//#pragma GCC optimize(3,"Ofast","inline")
#define re register
#define ll long long
#define ull unsigned long long

using namespace std;
//速读
inline int read()
{
	int x=0,f=1;char ch=getchar();
	while (!isdigit(ch)){if (ch=='-') f=-1;ch=getchar();}
	while (isdigit(ch)){x=x*10+ch-48;ch=getchar();}
	return x*f;
}
const int N = 11000005;
char a[N], s[N << 1];
int n, hw[N << 1], ans, l[N << 1], r[N << 1];
int main()
{
    ios::sync_with_stdio(false);
    //接收数据
    scanf("%s", a + 1);
    n = strlen(a + 1);
    //间隔插入字符------预处理
    s[0] = '#';
    s[1] = '$';
    int cnt = 1;
    for(int i = 1; i <= n; i++){
        s[++cnt] = a[i];
        s[++cnt] = '$';
    }
    n = (n << 1) + 2;
    s[n] = '~';
    //预处理可以注意的地方:格式为#$a$b$c$~
    //manacher
    int mr = 0, mid = 0;
    for(int i = 1; i <= n; i++){
        //如果在之前的最右回文半径之内
        if(i < mr){
            //对比之前的对称点和最右回文半径,取相对靠左的那个
            hw[i] = min(hw[(mid << 1) - i], mr - i);
        }
        //再往右的话就只能暴力了
        else{
            hw[i] = 1;
        }
        //暴力扩展
        while(s[i + hw[i]] == s[i - hw[i]]){
            ++hw[i];
        }
        //更新最大值
        if(hw[i] + i > mr){
            mr = hw[i] + i;
            mid = i;
        }
        //记录每个边界最大的回文距离
        r[i + hw[i] - 1] = max(r[i + hw[i] - 1], hw[i] - 1);
		l[i - hw[i] + 1] = max(l[i - hw[i] + 1], hw[i] - 1);
    }
    //递推,把前面的结果推到后面(因为前面更新的只是最大回文的情况,有些小回文会没有更新)
    for(int i = n; i >= 1; i -= 2){
        r[i] = max(r[i], r[i + 2] - 2);
    }
    for(int i = 1; i <= n; i += 2){
        l[i] = max(l[i], l[i - 2] - 2);
    }
    for(int i = 1; i <= n; i += 2){
        if(r[i] && l[i]){
            ans = max(ans, l[i] + r[i]);
        }
    }
    cout<<ans<<endl;
    return 0;
}

二、[国家集训队]拉拉队排练

题目描述

艾利斯顿商学院篮球队要参加一年一度的市篮球比赛了。拉拉队是篮球比赛的一个看点,好的拉拉队往往能帮助球队增加士气,赢得最终的比赛。所以作为拉拉队队长的楚雨荨同学知道,帮助篮球队训练好拉拉队有多么的重要。

拉拉队的选拔工作已经结束,在雨荨和校长的挑选下,n位集优秀的身材、舞技于一体的美女从众多报名的女生中脱颖而出。这些女生将随着篮球队的小伙子们一起,和对手抗衡,为艾利斯顿篮球队加油助威。

一个阳光明媚的早晨,雨荨带领拉拉队的队员们开始了排练。n个女生从左到右排成一行,每个人手中都举了一个写有26个小写字母中的某一个的牌子,在比赛的时候挥舞,为小伙子们呐喊、加油。

雨荨发现,如果连续的一段女生,有奇数个,并且他们手中的牌子所写的字母,从左到右和从右到左读起来一样,那么这一段女生就被称作和谐小群体。

现在雨荨想找出所有和谐小群体,并且按照女生的个数降序排序之后,前K个和谐小群体的女生个数的乘积是多少。由于答案可能很大,雨荨只要你告诉她,答案除以19930726的余数是多少就行了。

输入格式

输入为标准输入。

第一行为两个正整数n和K,代表的东西在题目描述中已经叙述。

接下来一行为n个字符,代表从左到右女生拿的牌子上写的字母。

输出格式

输出为标准输出。

输出一个整数,代表题目描述中所写的乘积除以19930726的余数,如果总的和谐小群体个数小于K,输出一个整数-1。

输入输出样例

输入 #1

5 3
ababa

输出 #1

45

说明/提示

样例说明

和谐小群体女生所拿牌子上写的字母从左到右按照女生个数降序排序后为ababa, aba, aba, bab, a, a, a, b, b,前三个长度的乘积为 。

数据范围与约定

测试点nK
11010
2-3100100
4-71,0001,000
8100,000= 1
9-11100,000100,000
12-14100,0001,000,000,000,000
15-17500,0001,000,000,000,000
181,000,000= 1
191,000,0001,000,000
201,000,0001,000,000,000,000

就是manacher模板,然后桶排序,再统计出前k即可

AC code

#include <bits/stdc++.h>
//#pragma GCC optimize(3,"Ofast","inline")
#define re register
#define ll long long
#define ull unsigned long long
#define r read()
using namespace std;
//速读
inline int read()
{
	int x=0,f=1;char ch=getchar();
	while (!isdigit(ch)){if (ch=='-') f=-1;ch=getchar();}
	while (isdigit(ch)){x=x*10+ch-48;ch=getchar();}
	return x*f;
}
const int N = 1000005;
ll n, k,mr = 0, center, rl[N << 1], tong[N << 1], ans = 1;

const int mod = 19930726;
char s[N], str[N << 1];
inline ll ksm(ll x, ll y){
    ll res = 1;
    for(; y; y >>= 1, x = x * x % mod){
        if(y & 1){
            res = res * x % mod;
        }
    }
    return res;
}
int main()
{
    ios::sync_with_stdio(false);
    int sum = 0;
    cin >> n>> k>> s + 1;
    for(re int i = 1; i <= n; i++){
        if(i <= mr){
            rl[i] = min(mr - i, rl[2 * center - i]);
        }
        else{
            rl[i] = 1;
        }
        while(rl[i] + i <= n && i - rl[i] >= 0 && s[i-rl[i]] == s[i+rl[i]]){
            rl[i]++;
        }
        if(rl[i] + i >= mr){
            mr = i + rl[i] - 1;
            center = i;
        }
        tong[rl[i] * 2 - 1]++;
    }
    if(n % 2 != 1){
        n--;
    }
    for(re int i = n; i >= 1; i -= 2){
        sum += tong[i];
        if(sum > k){
            ans = ans * ksm(i, k) % mod;
            cout<<ans<<endl;
            return 0;
        }else{
            ans = ans * ksm(i, sum) % mod;
            k -= sum;

        }
    }
    cout<<-1<<endl;
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值