「枚举」破碎的项链

本题为1月8日22寒假集训每日一题题解

题目来源:(未知)

题面

题目描述

你有一条由N个红色的,白色的,或蓝色的珠子组成的项链(3<=N<=350),珠子是随意安排的。这里是n=29的两个例子:

img

第一和第二个珠子在图片中已经被作记号。

图片A中的项链可以用下面的字符串表示:

brbrrrbbbrrrrrbrrbbrbbbbrrrrb .

假如你要在一些点打破项链,展开成一条直线,然后从一端开始收集同颜色的珠子直到你遇到一个不同的颜色珠子,在另一端做同样的事(颜色可能与在这之前收集的不同)。确定应该在哪里打破项链来收集到最大多数的数目的子。

举例来说,在图片A中的项链,可以收集到8个珠子,在珠子9和珠子10或珠子24和珠子25之间打断项链。在一些项链中,包括白色的珠子如图片B所示。当收集珠子的时候,一个被遇到的白色珠子可以被当做红色也可以被当做蓝色。

表现项链的字符串将会包括三符号r,b和w。写一个程序来确定从一条被供应的项链最大可以被收集珠子数目。

输入

第一行,N,珠子的数目

第二行,一串度为N的字符串,每个字符是r,b或w。

输出

单独的一行包含从被供应的项链可以被收集的珠子数目的最大值。

样例输入

29
wwwbbrwrbrbrrbrbrwrwwrbwrwrrb

样例输出

11

思路分析

本题的数据量只有350,所以可以直接枚举每一个分割点,然后记录最大的情况既可.

由于此处的珠子是一个环,所以我们可以在输入的字符串前后拼接两个一模一样的字符串,来模拟环的情况(这是在数据是环形时的常用技巧).不过这里要注意,此处的拷贝行为可能会导致后续统计左右段的时候出现区间重叠,此时最终统计出来的答案会比一个环的长度要长.不过既然能够重叠,就证明两边加起来可以走完整个环,此时把答案赋值成环的长度即可.

此处唯一的难点可能是对白色的特判处理,具体的看下面的参考代码即可,只要细心地考虑所有可能的情况就可以了.

参考代码

#pragma GCC optimize(1)
#pragma GCC optimize(2)
#pragma GCC optimize(3, "Ofast", "inline")
 
#include <iostream>
 
using namespace std;
 
int main()
{
    ios::sync_with_stdio(false);
 
    int n;
    cin >> n;
 
    string s;
    cin >> s;
    s += s + s; // 前后拼接两个一模一样的字符串来模拟环
 
    int res = 0;
    for (int i = n; i < 2 * n; i++) // 枚举每一个分割点
    {
        int len = 0; // 当前结果
        int l = i - 1; // 左段起始下标
        int r = i; // 右段起始下标
        char cL = s[l]; // 左段起始颜色
        char cR = s[r]; // 右段起始颜色

        // 左指针不断左移,直到越界或遇到不同的颜色为止
        // 如果起始颜色是白色,由于白色可以染成任何颜色,当前颜色是什么都可以移动,不过在移动后要把起始颜色更新成当前的颜色
        // 如果当前遇到的颜色是白色,由于白色可以染成任何颜色,所以可以当做与起始颜色一致,可以继续移动
        while (l >= 0 && (s[l] == cL || cL == 'w' || s[l] == 'w'))
        {
            len++;
            l--;
            if (cL == 'w') // 起始颜色是白色,需要把起始颜色更新成当前的颜色
            {
                cL = s[l];
            }
        }

        // 右指针不断右移,直到越界或遇到不同的颜色为止
        // 对白色的处理同理
        while (r < 3 * n && (s[r] == cR || cR == 'w' || s[r] == 'w'))
        {
            len++;
            r++;
            if (cR == 'w') // 起始颜色是白色,需要把起始颜色更新成当前的颜色
            {
                cR = s[r];
            }
        }
 
        len = min(len, n); // 如果出现区间重叠(当前结果超过n),需要把当前结果改为n
        res = max(len, res); // 记录最大值
    }
 
    cout << res << "\n";
 
    return 0;
}

"正是我们每天反复做的事情,最终造就了我们,优秀不是一种行为,而是一种习惯" ---亚里士多德

posted @ 2023-01-09 16:17  星双子  阅读(14)  评论(0编辑  收藏  举报