跳转至

孤独的照片

AcWing 4261

给定一个长度范围在 \(3 \leq n \leq n\) 的字符串,并且该字符串只会由 GH 这两种字符组成。

现在有一个任务就是将该字符串按照长度 \(k\) 划分成连续的子串 \(3 \leq k \leq n\),若 \(k\)\(3\) 开始一直枚举到 \(n\),请问划分成的连续子串中,有多少个含有孤独元素的?

孤独元素:在一个子串中,如果某个字符在该子串中只出现了 \(1\) 次,就说明该子串含有孤独元素。

输入样例

第一行输入一个整数 \(n\) 代表字符串的长度;

第二行输入一个字符串。

5
GHGHG

输出样例

输出一行,代表含有孤独元素的子串数量。

3

样例解释

\(k\) 的可能取值为 \(3、4、5\),所以可以切割成的子串有:

GHGHGHGHG
GHGHHGHG
GHGHG

含有孤独元素的子串是 GHG、HGH、GHG,总共有 \(3\) 个,所以输出 \(3\)

先分析特征

若该子串含有孤独元素,那么从该孤独子串开始,向左右两边延伸,字符都会是另外一个字符,例如对于该字串 GHG 来说,孤独元素 H,围绕在 H 周围的元素都是 G,这是一个孤独元素的特征。

若有这样的字符串 GGGGGHGGGGG,我们采取的策略是从孤独元素开始 i = 6,定义左右指针 l = r = i,左指针 li 开始往左边遍历到最左边且连续的 G,那么此时能构成孤独子串的个数有 i - l + 1 >= 3 ? i - l - 1 : 0 个;右指针 ri 开始往右边遍历,一直遍历到最右边且连续的 G,每遍历一次,此时能构成孤独子串的个数就有 r - i + 1 >= 3 ? i - l + 1 : r - l + 1 >= 3 ? r - l - 1 : 0

结果就是他们两个的和。

这只是一个孤独字符的情况,多个的情况下只需按照相同的逻辑一一遍历即可。

代码参考
#include <iostream>

using namespace std;
const int N = int (1e7 + 10);

long long n, res; // 结果会爆 int
char a[N];

int main(void)
{
    scanf ("%lld", &n);
    scanf ("%s", a + 1);
    int l, r;
    for (int i = 1; i <= n; i ++)
    {
        l = r = i;
        while (l - 1 > 0 && a[l - 1] != a[i])  l --;
        if (i - l + 1 >= 3) res += i - l - 1; // 左指针遍历到最左边时,能构成孤独子串的个数
        while (r + 1 <= n && a[r + 1] != a[i]) 
        {
            r ++; // 右指针每向右遍历一次,能构成孤独子串的个数
            if (r - i + 1 >= 3) res += i - l + 1;
            else if (r - l + 1 >= 3) res += r - l - 1;
        }
    }
    printf ("%lld\n", res);
}