4993.FEB(分类讨论+贪心)
一、题面描述
有一个长度为N的字符串 S,其中的每个字符要么是 B
,要么是 E
。
我们规定 S的价值等于其中包含的子串 BB
以及子串 EE
的数量之和。
例如,BBBEEE
中包含 22 个 BB
以及 22 个 EE
,所以 BBBEEE
的价值等于 44。
我们想要计算 S的价值,不幸的是,在我们得到 S 之前,约翰将其中的一些字符改为了 F
。
目前,我们只能看到改动后的字符串 S,对于其中的每个 F
,我们并不清楚它之前是 B
还是 E
。
请你计算,改动前的 S 有多少种可能的价值并将所有可能价值全部输出。
输入格式
第一行包含一个整数 N。
第二行包含改动后的字符串 S。
输出格式
第一行输出一个整数 K,表示改动前的 S的可能价值的数量。
接下来 K行,按照升序顺序,每行输出一个可能价值。
数据范围
1≤N≤2×105
输入样例1:
4
BEEF
输出样例1:
2
1
2
输入样例2:
9
FEBFEBFEB
输出样例2:
2
2
3
输入样例3:
10
BFFFFFEBFE
输出样例3:
3
2
4
6
二、题目分析
-
时间复杂度
- N = 2e5,在时间复杂度为**O(nlogn)**内
-
样例模拟
- 通过模拟样例,试图通过分类讨论对B,E,F进行处理,假设B=0, E=1, F=X,k表示x的数量
- 情况一:xxxxxxxx:共有k种情况,0,1,2,3···,k-1 d = 1的等差数列
- 情况二:0xxxxxxx:共有k+1种情况(同理于1xxxxxxx,xxxxxxx1,xxxxxxx0),0,1,2,3,···,k-1, d = 1的等差数列
- 情况三:0xxxxxx0:k+1,k-1,k-3,k-5····· d=2的等差数列
- 情况四:0xxxxxx1:
- 当k为奇数时,举例k=5,0xxxxx1时,每一位与前一位取不同值时,有最小值值min = 1,从k,k-2,···1的d=2的等差数列
- 当k为偶数时,举例k=6,0xxxxxx1时,每一位与前一位取不同值时,有最小值值min=0, 从k,k-2, ··· 0的d=2的等差数列
-
等差数列合并
- 合并两个d为2的等差数列,仍然会得到一个公差为2的等差数列
- a1,a2,a3,··· an 与 b1,b2,b3 ··· bn,合并为a1+b1,···,a1+bn,b1+a1,···,b1 + an
- 合并两个d分别为2和1的等差数列,可以取得an + bm,an + bm-1, an-1 + bm-1,···a1 + b1,d为1的等差数列
- 合并两个d为2的等差数列,仍然会得到一个公差为2的等差数列
-
方法步骤
- 第一步,先求中间的段
- 第二步,求两端的段
- 如果第二步存在,d=1,否则d=2
#include<bits/stdc++.h>
using namespace std;
int main(){
int n;
string str;
cin >> n >> str;
if(str == string(n, 'F')){
cout << n << endl;
for(int i = 0; i < n; i ++)
cout << i << endl;
}
else {
int l = 0, r = n - 1;
while(str[l] == 'F') l ++;
while(str[r] == 'F') r --;
int low = 0, high = 0;
auto s = str;
for(int i = l; i <= r; i ++){
if(s[i] == 'F' ){
if(s[i - 1] == 'B') s[i] = 'E';
else s[i] = 'B';
}
if(i > l && s[i] == s[i - 1]) low ++;
}
s = str;
for(int i = l; i <= r; i ++){
if(s[i] == 'F') s[i] = s[i - 1];
if(i > l && s[i] == s[i - 1]) high ++;
}
int ends = l + n - 1 - r, d = 2;
if(ends) high += ends, d = 1;
cout << (high - low) / d + 1 << endl;
for(int i = low; i <= high; i += d)
cout << i << endl;
}
}
三、总结
本题重在分类讨论,核心是找到d=2和d=1的等差数列,没做出来的原因是未考虑到连续F对本题的作用。