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

相关推荐
乔冠宇15 分钟前
环状 DNA 序列的最小表示法
java·数据结构·算法
vir0225 分钟前
找出目标值在数组中的开始和结束位置(二分查找)
数据结构·c++·算法·leetcode
星沁城29 分钟前
73. 矩阵置零
java·算法·矩阵
Sweet_vinegar37 分钟前
变异凯撒(Crypto)
算法·安全·ctf·buuctf
小刘|1 小时前
《Java 实现选择排序:原理剖析与代码详解》
java·算法·排序算法
CodeHackerBhx2 小时前
归并排序和随机化快速排序
数据结构·算法·排序算法
摆烂小白敲代码2 小时前
背包九讲——背包问题求具体方案
c语言·c++·算法·背包问题·背包问题求具体方案
Matlab程序猿小助手2 小时前
【MATLAB源码-第208期】基于matlab的改进A*算法和传统A*算法对比仿真;改进点:1.无斜穿障碍物顶点2.删除中间多余节点,减少转折。
开发语言·嵌入式硬件·算法·matlab·机器人
时髦的琉璃682 小时前
GHuNeRF: Generalizable Human NeRF from a Monocular Video
算法
3 小时前
开源竞争-大数据项目期末考核
大数据·人工智能·算法·机器学习