写在前面
题目来源:AcWing 寒假每日一题2023活动
 链接:https://www.acwing.com/problem/content/description/4264/
题目
Farmer John 最近购入了 N 头新的奶牛,每头奶牛的品种是更赛牛(Guernsey)或荷斯坦牛(Holstein)之一。
奶牛目前排成一排,Farmer John 想要为每个连续不少于三头奶牛的序列拍摄一张照片。
然而,他不想拍摄这样的照片,其中只有一头牛的品种是更赛牛,或者只有一头牛的品种是荷斯坦牛——他认为这头奇特的牛会感到孤立和不自然。
在为每个连续不少于三头奶牛的序列拍摄了一张照片后,他把所有「孤独的」照片,即其中只有一头更赛牛或荷斯坦奶牛的照片,都扔掉了。
给定奶牛的排列方式,请帮助 Farmer John 求出他会扔掉多少张孤独的照片。
如果两张照片以不同位置的奶牛开始或结束,则认为它们是不同的。
输入格式
 输入的第一行包含 N。
输入的第二行包含一个长为 N 的字符串。如果队伍中的第 i 头奶牛是更赛牛,则字符串的第 i 个字符为 G。否则,第 i 头奶牛是荷斯坦牛,该字符为 H。
输出格式
 输出 Farmer John 会扔掉的孤独的照片数量。
数据范围
 3≤N≤5×105
 输入样例:
 5
 GHGHG
 输出样例:
 3
 样例解释
 这个例子中的每一个长为 3 的子串均恰好包含一头更赛牛或荷斯坦牛——所以这些子串表示孤独的照片,并会被 Farmer John 扔掉。
所有更长的子串(GHGH、HGHG 和 GHGHG)都可以被接受。
我的超时代码 过了11/12的数据
/**
 * 2023年1月12日15:04:41——2023年1月12日16:08:20
 二分类,那用0和1分别表示G 和 H,用前缀和的思想做!
 * 灵光乍现
 * 时间复杂度为:O(N*N) 10次方量级,超时啦,但是这是目前想到最优的了
 * 结果:通过11/12的数据  哈哈  还行
 * 
 * 再思考一下怎么优化!
 * 2023年1月12日16:08:27——
 */
 
#include <iostream>
using namespace std;
const int N = 5 * 1e5 + 10;
char c;
int a[N], s[N];   // a是0 1数组,s是前缀和数组下标从1开始,为了省去特判
int main()
{
    int n;
    scanf("%d", &n);
    scanf("%c", &c);    // 吃缓冲区的回车
    for (int i = 0; i < n; i ++ ) 
    {
        scanf("%c", &c);
        if (c == 'H') a[i + 1] = 1;
        else a[i + 1] = 0;
    }
    // 计算数组a的前缀和,O(N)
    for (int i = 1; i <= n; i ++ ) s[i] = s[i - 1] + a[i];
    
    // 测试开始
    // for (int i = 1; i <= n; i ++ ) cout << a[i] << " ";
    // cout << endl;
    // for (int i = 1; i <= n; i ++ ) cout << s[i] << " ";
    // 测试结束
    
    long long cnt = 0;
    // 遍历长度为3,4,5,,,n
    for (int len = 3; len <= n; len ++ ) 
    {
        for (int i = 1; i + len - 1 <= n; i ++ )
        {
            int sum_sequence = s[i + len - 1] - s[i - 1];  // 从i这个起点开始的长度为len的序列和为sum_sequence
            int cnt_H = sum_sequence, cnt_G = len - cnt_H;  // H的个数就是1的个数,G的个数是剩余的
            if (cnt_H == 1 || cnt_G == 1) cnt ++ ;
        }
    }
    printf("%lld\n", cnt);
    
    return 0;
}
听完Y总讲解之后
思路
总的思路:枚举只包含一个孤独字母的连续序列,就能不重不漏。
 枚举:
 情况1——第0头牛当孤独牛,有几张孤独照片
 情况2——第1头…
 情况3——第2头…
 情况4——第3头…
 …
 求和上述情况。
 
 注:图片来自AcWing
 注:L和R必须是连续出现的H 的个数,碰到H就要停止,因为不连续就会引入新的G,导致G的个数大于1个啦,G就不孤独了,G就有小伙伴G了
一开始理解错了,噌噌噌写了一堆,然后Wrong Answer哈哈。
 看了一下题解,知道错在哪了(/捂脸)
l数组:表示当前位置左边有连续的几个异类牛
 r:右边,同上
代码2
自己理解完的AC代码,缺点是找左右的异类牛数目需要循环,太慢,但是第12个50000的数据倒是过了
#include <iostream>
using namespace std;
const int N = 5*1e6 + 10;
char c[N];
int main()
{
    int n;
    scanf("%d", &n);
    scanf("%c", &c);    // 吃缓冲区的回车
    for (int i = 0; i < n; i ++ ) scanf("%c", &c[i]);
    
    long long int ans = 0;
    for (int i = 0; i < n; i ++ ) 
    {
        // 往左走,有几个连续的 不同种类的牛牛
        int cnt_left = 0;
        for (int l = i - 1; l >= 0; l -- ) 
        {
            if (c[l] == c[i]) break;    // 碰到同类牛牛,跳出循环,不连续了
            else cnt_left ++ ;  // 不同类牛牛,加上
        }
        
        // 往右走,有几个连续的 不同种类的牛牛
        int cnt_right = 0;
        for (int r = i + 1; r < n; r ++ ) 
        {
            if (c[r] == c[i]) break;    // 碰到同类牛牛,跳出循环,不连续了
            else cnt_right ++ ;  // 不同类牛牛,加上
        }
        
        // 统计个数
        ans =  ans + max(cnt_right-1, 0) + max(cnt_left-1, 0) + ((long long)cnt_right * (long long)cnt_left);
        
        // cout << i << "   这是一个循环   " << ans << endl;
    }
    
    printf("%lld\n", ans);
    
    return 0;
}
代码 3 Y总的思路
用y总思路的L[ ]数组和R[ ]数组
L数组是什么?L[i] :第i头牛左侧的连续异类牛个数
 R数组同理,第i头牛右侧的连续异类牛个数。
得到L R数组即可,分别需要一层循环。



















