寒假每日一题2023——4261. 孤独的照片

写在前面

题目来源: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 

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头…

求和上述情况。
寒假每日一题2023——4261. 孤独的照片_第1张图片
注:图片来自AcWing
注:L和R必须是连续出现的H 的个数,碰到H就要停止,因为不连续就会引入新的G,导致G的个数大于1个啦,G就不孤独了,G就有小伙伴G了

一开始理解错了,噌噌噌写了一堆,然后Wrong Answer哈哈。
看了一下题解,知道错在哪了(/捂脸)

l数组:表示当前位置左边有连续的几个异类牛
r:右边,同上

代码2

自己理解完的AC代码,缺点是找左右的异类牛数目需要循环,太慢,但是第12个50000的数据倒是过了

#include 

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数组即可,分别需要一层循环。

你可能感兴趣的:(AcWing,c++)