玩命加载中 . . .

Dominant Character


传送门

https://codeforces.com/contest/1605

题目简述

给定一个仅由a,b,c组成的字符串,找出这样的最小长度的子串:

  • 子串长度至少为2
  • 子串中a的个数严格大于b的个数和c的个数

并输出该最小长度。

题目分析

首先我们容易想到,要找出这样的字符串,无非就需要找出两个a之间的b、c的个数是否满足上述条件。因此,需要开数组存储b、c的个数:
b[i]表示字符串中第i号位置及其以前的b的个数总数,c[i]类似。

思路很直接,但是字符串中a的位置可能很多,总不可能任意两个都来验证一遍吧。下面就是关键了:
仔细分析后,可以发现,如果有这样的子串,那么子串内部的a的个数至多为3,至少为2。

以四个a为例来简单证明:
有满足条件的子串,形如 a…a…a…a,约定1、2、3号空位,B为b的个数,C为c的个数,
B_1+B_2+B_3 < 4, C_1+C_2+C_3 < 4 这俩条件必须成立
而B_1>1, C_1>1; B_2>1, C_2>1; B_3>1, C_3>1这三组条件中每组至少有一个成立,显然与上一个条件矛盾了

也就是说,我们只需要验证相邻两个a之间、相隔一个a的两个a之间即可,即可以存储当前a的位序和上一个a、上上个a的位序。

代码实现

#include <iostream>
#include <cstdio>
#include <string>
#define N 1000005
using namespace std;
int b[N], c[N];
char str[N];
int t, n;

int seekString()
{
    // 约定:i为当前a位置,la为上一个a位置,lla为上上个a位置
    int la = 0, lla = 0;
    int res = -1;
    for (int i = 1; i <= n; i++)
    {
        if (str[i] == 'a')
        {
            if (la != 0 && b[i] - b[la] < 2 && c[i] - c[la] < 2 && (res == -1 || res > i - la +1))
            {
                res = i - la + 1;
            }
            if (lla != 0 && b[i] - b[lla] < 3 && c[i] - c[lla] < 3 && (res == -1 || res > i - lla + 1))
            {
                res = i - lla + 1; 
            }
            // 位置更新
            lla = la;
            la = i;
        }
    }
    return res;
}

int main()
{
    cin >> t;
    for (int i = 1; i <= t; i++)
    {
        cin >> n;
        scanf("%s", str + 1);
        for (int j = 1; j <= n; j++)
        {
            b[j] = b[j - 1] + (str[j] == 'b');
            c[j] = c[j - 1] + (str[j] == 'c');
        }
        cout << seekString() << endl;
    }
}

文章作者: 鹿卿
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 鹿卿 !
评论
  目录