FEB(acwing)

FEB

题目描述

有一个长度为 N的字符串 S,其中的每个字符要么是 B,要么是 E。
我们规定 S的价值等于其中包含的子串 BB 以及子串 EE 的数量之和。
例如,BBBEEE 中包含 2个 BB 以及 2个 EE,所以 BBBEEE 的价值等于 4。
我们想要计算 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

代码

#include<bits/stdc++.h> 
using namespace std;

int n; // 声明一个整型变量n来存储字符串的长度
string s; // 声明一个字符串变量s来存储输入的字符串

int main() {
    cin >> n >> s; // 从标准输入读取字符串的长度n和字符串s

    // 情况一:如果字符串s完全由字符'F'组成
    if(s == string(n, 'F')) {
        cout << n << endl; // 输出n,可能的价值数量为字符串长度
        for(int i = 0; i < n; i++)
            cout << i << endl; // 按顺序输出从0到n-1的整数
    }
    else {
        int l = 0, r = n - 1;
        // 跳过字符串s左边的'F'字符
        while(s[l] == 'F') l++;
        // 跳过字符串s右边的'F'字符
        while(s[r] == 'F') r--;

        int min = 0, max = 0; // 初始化最小和最大价值为0
        auto str = s;
        // 估算最小价值
        for(int i = l; i <= r; i++) {
            if(str[i] == 'F') {
                // 如果F的前一个字符是B,则假设F是E,反之亦然
                if(str[i - 1] == 'B') str[i] = 'E';
                else str[i] = 'B';
            }
            // 计算相邻字符相同的情况,增加最小价值
            if(i > l && str[i] == str[i - 1]) min++;
        }
        
        str = s;
        // 估算最大价值
        for(int i = l; i <= r; i++) {
            // 将F替换为它前面的字符(B或E)
            if(str[i] == 'F') str[i] = str[i - 1];
            // 计算相邻字符相同的情况,增加最大价值
            if(i > l && str[i] == str[i - 1]) max++;
        }
        //计算左右两边的最大价值 
        //左边:0~l-1,共l-1-0+1=l个数,例:0~4-1,0,1,2,3共4个数,4-1+1 
        //右边:r+1~n-1 共n-1-(r+1)+1=n-1-r个数 
        int ends = l + n - 1 - r, d = 2;
        // 调整最大价值,并确定输出价值的间隔
        if(ends) max += ends, d = 1;//如果ends不为0,说明公差为1和公差为1的等差数列合并,公差为1,反之,ends为0,说明两边没有F段,公差为2
		//并求总等差数列的最大值,最小值不变,因为两边的等差数列最小值=0 
        
        cout << (max - min) / d + 1 << endl; // 输出总的价值数量
        for(int i = min; i <= max; i += d) 
            cout << i << endl; // 逐个输出每个可能的价值
    }
    return 0; 
}

题解

第一步,先分析每一段连续的x的价值有哪些种。
第二步,再分析所有段的价值之和有哪些。

k在下面表示x的数量

情况1:xxxxxx:0,1,2,…,k-1

在这里插入图片描述

情况2:0xxxxxx:0,1,2,…,k

在这里插入图片描述

情况3:0xxxxxx0:k+1,k-1,k-3,k-5,…

在这里插入图片描述
最大值:k+1
最小值:

  • k为偶数:min=1
  • k为奇数,min=0
讨论中间的值能否都取到:

任何一个方案都可以通过全0的方案变换过来
全0的方案,最大值为k+1
变了中间任意一个数(每改变一位),在原有的基础上±两个1,k+1±1±1
新价值=原价值±1±1,奇偶性一样
所以,所有的情况是:k+1,k-1,k-3,k-5,…
如果k=5,max=6,6-2=4,4-2=2,2-2=0;
在这里插入图片描述

情况4:0xxxxxx1:k,k-2,k-4,…

在这里插入图片描述
最大值:k
最小值:

  • k为偶数:0
  • k为奇数:1

等差数列的合并

中间(情况3、4)的等差数列(公差为2)的合并

合并两个公差为2的等差数列,仍然会得到一个公差为2的等差数列,最小值为两个等差数列最小值相加,最大值为两个等差数列最大值相加,中间所有公差为2的数都取得到。
在这里插入图片描述
总价值数量为(17-5)/2+1=7
总价值数量公式:(max-min)/d+1

边(情况2)与边(情况2)的等差数列(公差为1)合并

合并一个公差为1的等差数列,得到一个公差为1的等差数列,最小值为两个等差数列最小值相加,最大值为两个等差数列最大值相加,中间所有公差为1的数都取得到。

边(情况2:公差为1)和中间(情况3,4:公差为2)的等差数列合并

合并一个公差为2的等差数列和公差为1的等差数列,得到一个公差为1的等差数列,最小值为两个等差数列最小值相加,最大值为两个等差数列最大值相加,中间所有公差为1的数都取得到。
在这里插入图片描述
总价值数量为(15-7)/1+1=9
总价值数量公式:(max-min)/d+1

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

命运从未公平

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值