题意:
咕咕东很聪明,但他最近不幸被来自宇宙的宇宙射线击中,遭到了降智打击,他的英语水平被归零了!这一切的始作俑者宇宙狗却毫不知情!
此时咕咕东碰到了一个好心人——TT,TT在吸猫之余教咕咕东学英语。今天TT打算教咕咕东字母A 和字母B,TT给了咕咕东一个只有大写A、B组成的序列,让咕咕东分辨这些字母。
但是咕咕东的其他学科水平都还在,敏锐的咕咕东想出一个问题考考TT:咕咕东问TT这个字符串 有多少个子串是Delicious的。
TT虽然会做这个问题,但是他吸完猫发现辉夜大小姐更新了,不想回答这个问题,并抛给了你, 你能帮他解决这个问题吗?
Delicious定义:对于一个字符串,我们认为它是Delicious的当且仅当它的每一个字符都属于一个 大于1的回文子串中。
思路:
首先要分析什么样的子串是Delicious的:该字符串每个字符都属于一个大于1的回文子串中。可知AA、BB、ABA、BAB等都是Delicious的。再多写一写就会发现规律,当AB或者BA(其中一个字母只有一个,另一个字母大于等于一个)时才不是Delicious的。所以应遍历字符串,根据发现的规律来进行判断。
数据范围最大为3*10^5,不能求出每一个子串在根据规律来判断。应该进行优化。于是想到在求子串时可以记录A与B的个数numA,numB,以及ABAB序列的个数sum(如AAABBA就有3个ABA序列),在判断子串是否满足Delicious时,可根据这三个信息来判断。
对长度大于一的子串进行判断:如果全A或全B(即sum=1),则一定Delicious;如果是AB型,AB的个数都大于一,则Delicious,否则不Delicious;如果是ABA或BAB型,此时它一定Delicious,而且在它的基础上形成的后续子串也一定Delicious,于是可以在此进行剪枝。
总结:
一道数据范围很大的题目,对应CSP T4,需要好好对复杂度进行优化。一开始想到O(n2)的算法,然后在之后优化时想到了以上的剪枝。因为数据是随机生成的比较水,剪枝的算法就能直接过。如果极端数据如全是A,就无法剪枝,这样就是O(n2)的时间复杂度,会超时。同时应注意答案应该是long long int。
代码:
#include <iostream>
using namespace std;
int n;
char s[300010];
long long int ans=0;
int main()
{
cin>>n;
cin>>s;
for(int i=0; i<n-1; i++)
{
char now=s[i];
int sum=1; //ABAB...的个数
int numA=0,numB=0; //A/B的个数
if(s[i]=='A') numA++;
else numB++;
for(int j=i+1; j<n; j++)
{
if(s[j]=='A') numA++;
else numB++;
if(s[j]!=now)
sum++,now=s[j];
//判断是否满足
if(sum==1) ans++;
else if(sum==2&&numA!=1&&numB!=1)
ans++;
else if(sum==3) //后续一定满足
{
ans=ans+n-j;
break;
}
}
}
cout<<ans<<endl;
}