标题:切开字符串
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
资源约定:
峰值内存消耗 < 512M
CPU消耗 < 1000ms
分析
首先,正回文子串是长度为奇数的回文子串,它的条件有两个,一是字符串必须是回文串,二是长度为奇数;然而非正回文子串,也有两种情况,一是根本不是回文串,二是长度为偶数的回文串。接下来就是怎么找,从左到右遍历,记录下每个位置的正回文子串的个数,这里用了set,自动去重,省去了自己对比的时间。从右到左遍历,记录每个位置的非正回文子串的个数,这里可以与正回文子串的set对比,如果在里面,就说明是正回文子串,如果不在里面,就说明是非正回文子串。用了两个数组分别记录每个位置的正回文子串和非正回文子串的数量,最后求最大值就ok了。
代码
#include<iostream>
#include<string>
#include<set>
using namespace std;
typedef long long ll;
const int N=100000;
bool judge(string str)//判断是否为回文串
{
for(int i =0;i<str.size()/2;i++)
{
if (str[i]!=str[str.size()-i-1])
return false;
}
return true;
}
int main ()
{
int n ;
string str,temp;
cin>>n;
cin>>str;
ll zheng[N];//用于每个位置存储正回文子串的个数
ll fei[N];//用于每个位置存储非正回文子串的个数
set<string> e,ne;
for (int i=0;i<n;i++)//求正回文的个数
{
for(int j=i;j>=0;j-=2)//j-=2
{
int t=i-j+1;
temp=str.substr(j,t);
int flag =judge(temp);
if(flag)
e.insert(temp);
}
zheng[i]=e.size();
}
for (int i=n-1;i>=0;i--)//求非正文子串
{
for(int j=i;j<n;j++)
{
//判断是否是正回文子串
int flag=0;
int t=j-i+1;
temp=str.substr(i,t);
if(temp.size()%2)
if(e.find(temp)!=e.end())//判断是否在正回文子串的集合里面
flag=1;
if(!flag)
ne.insert(temp);
}
fei[i]=ne.size();
}
ll ans=0;
for(int i=1;i<n;i++)
ans=max(ans,zheng[i-1]*fei[i]);
cout<<ans<<endl;
return 0;
}