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×10^5^

输入样例1:

4

BEEF

输出样例1:

2

1

2

输入样例2:

9

FEBFEBFEB

输出样例2:

2

2

3

输入样例3:

10

BFFFFFEBFE

输出样例3:

3

2

4

6

代码

cpp 复制代码
#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: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

相关推荐
川石课堂软件测试8 分钟前
UI自动化测试|web端元素获取&元素等待实践
开发语言·前端·功能测试·算法·ui
ahadee20 分钟前
蓝桥杯每日真题 - 第20天
c语言·算法·蓝桥杯·bfs
I AM_SUN2 小时前
Leetcode:15.三数之和
算法·leetcode·职场和发展
ahadee2 小时前
蓝桥杯每日真题 - 第21天
c语言·算法·蓝桥杯
蓝莓星冰乐3 小时前
数据结构-二叉树_堆
数据结构·算法
张彦峰ZYF3 小时前
接口性能优化宝典:解决性能瓶颈的策略与实践
java·redis·分布式·后端·算法·性能优化·架构
阿史大杯茶5 小时前
CodeTON Round 9 (Div. 1 + Div. 2, Rated, Prizes! ABCDE题) 视频讲解
数据结构·c++·算法
赔罪5 小时前
C 语言变量说明符
c语言·开发语言·c++·学习·算法·objective-c
A Man Of Mould6 小时前
【数据结构】—— 堆
数据结构·算法
LNTON羚通6 小时前
视频智能分析软件LiteAIServer摄像机实时接入分析平台噪声监测算法介绍
人工智能·算法·音视频