标题:切开字符串
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