【问题描述】
给定一个由字符'a'和字符'b'组成的字符串,可以删除若干字符,使得剩下来的字符串满足前后段为a,中间段为b(aaa....aaabbbb.....bbbbaaa.....aaa),区段可以没有字符(ba,ab,b,aa都是合法的),求最长剩下字符串的长度。
【输入形式】
输入为一行一个长度不超过5000的非空字符串,字符串仅由字符'a'和字符'b'组成。
【输出形式】
输出为一个整数,表示符合要求的最长剩下字符串长度
【样例输入1】
abba
【样例输出1】
4
【样例输入2】
bab
【样例输出2】
2
【思路解析】
暴力(80分)
- 初看题是有点懵的。我们可以把合法字符串看成三个部分:第一部分为前面的全是a的部分;第二部分为中间全是b的部分;第三部分为后面全是a的部分。
- 所以我们可以想到一个暴力一点的解法:用两层循环遍历三个部分的所有可能区间,计数三个区间内不满足要去的字符个数,相加得到要删除的个数,找出最小删除个数即可。
代码如下:
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
string s;
int main()
{
cin>>s;
int len=s.length();
int min_del=999999;
//时间复杂度为O(len^3)
for(int i=0;i<len;i++){
for(int j=i;j<len;j++){
int del=0;
//任何一个部分都有可能是空
//第一部分[0,i)
for(int k=0;k<i;k++){
if(s[k]=='b') del++;
}
//第二部分[i,j]
if(i!=j){
for(int k=i;k<=j;k++){
if(s[k]=='a') del++;
}
//如果i==j则表示第二部分为空
}
//第三部分(j,len-1]
for(int k=j+1;k<=len-1;k++){
if(s[k]=='b') del++;
}
min_del=min(min_del,del);
}
}
cout<<len-min_del<<endl;
return 0;
}
思路不难,唯一要注意的就是中间区间的处理。
-
代码中也说复杂度为O(len^3) ,太大,需要优化。
前缀和(100分)
- 暴力法的思路没有问题,要想解决复杂度的问题要从分别统计三个区间内不满足的字符数入手
- 既然是区间内的操作,可以联想到一种方法:前缀和
- 设置一个前缀和数组用来统计区间【0,i】内的字符a的数目,这样在统计三个区间内不满足的字符数时可以从遍历变为查询。
代码如下:
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
string s;
//前缀和 统计 区间段的数目
int cnt[5010];//统计[0,i]内的a的个数
int main()
{
cin>>s;
int len=s.length();
for(int i=0;i<len;i++){
if(s[i]=='a'){
if(i==0) cnt[i]=1;
else cnt[i]=cnt[i-1]+1;
}
else cnt[i]=cnt[i-1];
}
// for(int i=0;i<len;i++){
// cout<<cnt[i]<<" ";
// }
int min_del=999999;
//时间复杂度为O(len^2)
for(int i=0;i<len;i++){
for(int j=i;j<len;j++){
int del=0;
//任何一个部分都有可能是空
//第一部分[0,i)
if(i!=0) del+=(i-cnt[i-1]);
//第二部分[i,j]
if(i!=0) del+=(cnt[j]-cnt[i-1]);
else del+=(cnt[j]-cnt[i]);
//第三部分(j,len-1]
if(j!=len-1)del+=((len-1-j)-(cnt[len-1]-cnt[j]));
min_del=min(min_del,del);
}
}
cout<<len-min_del<<endl;
return 0;
}
总体来说是一个很有趣的题,不看标签我都可能想不到使用前缀和。