15年国赛 切开字符串

该博客探讨了一道编程竞赛题目,要求找到一种方法将字符串切成两段,使得两段中正回文子串与非正回文子串的乘积最大。文章详细解释了解决方案,包括使用Manacher算法求解回文半径,通过后缀数组计算不同回文串的数量,并动态维护后缀子串的不同数量。算法复杂度为O(Nlog^2N),对于不同数据范围有不同的解法策略。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

标题:切开字符串

Pear有一个字符串,不过他希望把它切成两段。
这是一个长度为N(<=10^5)的字符串。
Pear希望选择一个位置,把字符串不重复不遗漏地切成两段,长度分别是t和N-t(这两段都必须非空)。

Pear用如下方式评估切割的方案:
定义“正回文子串”为:长度为奇数的回文子串。
设切成的两段字符串中,前一段中有A个不相同的正回文子串,后一段中有B个不相同的非正回文子串,则该方案的得分为A*B。

注意,后一段中的B表示的是:“...非正回文...”,而不是: “...正回文...”。
那么所有的切割方案中,A*B的最大值是多少呢?

【输入数据】
输入第一行一个正整数N(<=10^5)
接下来一行一个字符串,长度为N。该字符串仅包含小写英文字母。
【输出数据】
一行一个正整数,表示所求的A*B的最大值。
【样例输入】
10
bbaaabcaba
【样例输出】
38
【数据范围】
对于20%的数据,N<=100
对于40%的数据,N<=1000
对于100%的数据,N<=10^5

题比较难,有一定思维难度,而且代码实现复杂。首先,我们肯定要求出每个位置为中心的回文半径,这个可以用Manacher算法或Hash。接下来,我们需要求出每个前缀有多少不同的回文串(这里提到的所有回文串都是“正回文串”即长度为奇数),然后求出后缀有多少不同的回文串,和后缀有多少不同的子串。对于回文串的问题,由Manacher算法的引理可知,不同的回文串个数是O(N)的,因此我们可以暴力找出所有回文串,用一个set维护Hash值。接下来,求出这些子串在原串中第一次和最后一次出现的位置,就可以知道前缀和后缀分别有多少不同的回文串。这一步可以先对原串构建后缀数组,然后在后缀数组中这个子串对应了连续的一段,求区间RMQ即可得到第一次、最后一次出现位置。然后处理“后缀的不同子串”数量。我们知道,如果是求一个完整字符串的不同子串,则构建后缀数组后,总子串数-Σ(相邻两项的LCP)就是答案。因此这儿我们考虑从后往前逐个插入后缀,动态维护当前答案——用一个平衡树或者set维护目前为止所有后缀的序列,每当插入一个字符串时,只有O(1)个相邻的LCP被改动,可以通过lower_bound找出前驱后继然后维护有关操作。整个算法复杂度为O(Nlog^2N),瓶颈在查找“后缀数组中连续的一段”那边。对于20%的数据是完全暴力,对于40%的数据也不难设计回文半径、不同子串的O(N^2)算法。

#include <cstdio>
#include <iostream>
#include <cstring>
#include <cstdlib>
#include <cmath>
#include <set>
#include <algorithm>
#include <map>
#include <bitset>
#include <vector>
#include <queue>
#include <stack>
#include <utility>
#include <functional>
#include <sstream>
#include <list>
#include <complex>
#include <ctime>

#d
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值