【AcWing每日一题】4261. 孤独的照片

2023-11-05 11:20
文章标签 每日 acwing 照片 孤独 4261

本文主要是介绍【AcWing每日一题】4261. 孤独的照片,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

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)都可以被接受。

自己的思路:

  • 输入字符串之后记录两种牛的种类的数量
  • 对于每一个子字符串,查看这个字符串剩下的牛的数量是否符合
  • 如果某一种牛的数量为1,那么这一组照片应该被扔掉
#include<iostream>
using namespace std;const int N = 5e5+10;
int n, h = 0, g = 0, res = 0, sum = 1;
char str[N];
int main(){	cin >> n;scanf("%s", str);for(int i = 0; i < n; i++){		if(str[i] == 'H') h++;if(str[i] == 'G') g++;} int sumh = h, sumg = g;for(int i = 0; i < n; i++){int sg = sumg, sh = sumh; for(int j = n-1; j >= i+2; j--){if(sg == 1 || sh == 1)		//如果其中有一种牛的数量是1 res++;					//扔掉的孤独的照片数量加一 if(sg == 0 || sh == 0)		//如果某一种牛的数量为0 break;					//没有能使得照片扔掉的序列,剪枝 //如果没有牛的数量是1if(str[j] == 'G') sg--;		//把后面这种牛的数量减掉,指针前移 if(str[j] == 'H') sh--;}if(str[i] == 'G') sumg--;		//把前面这种牛的数量减掉,指针后移 if(str[i] == 'H') sumh--;}	cout << res;	return 0;
}

问题很显然,这样会在任何情况下的时间复杂度都是O(n2)
这样在内层的for循环加入剪枝

if(sg == 0 || sh == 0)		//如果某一种牛的数量为0 break;					//没有能使得照片扔掉的序列,剪枝 

会在部分较好情况下把时间复杂度降一点,最好的时间复杂度为O(n)
但是在较坏的情况下,还是有很高的复杂度
还有个问题就是res的数量级用int是存不下的,需要long long

//测试复杂度的代码:cin >> n;
//	scanf("%s", str);for(int i = 0; i < n; i++){	if(i%2 == 0) str[i] = 'G';else str[i] = 'H';
//		str[i] = 'H';	if(str[i] == 'H') h++;if(str[i] == 'G') g++;} 

y总的方法:

  • 因为要扔掉的图片中有一个字母只出现一次,所以我们可以去枚举只出现一次的图片是哪一个
  • 我们要找的是,只包含一个G或H的、长度大于等于三的字串的数量
  • 符合要求的字符串只能包含一个G或H,所以其他位置是另外一个字母
  • 枚举每一个位置,会出现三种情况

对于每一个位置的字母,在字串的长度大于等于三的情况下

  1. 左边和右边至少存在一个不同于当前位置的字母。设左边有L个,右边有R个,那么左边和右边分别可以取1,2,…,L个,1,2,…,R个(这种情况至少一边取一个)。那么这种情况符合乘法原理,一共是L*R种情况。
  2. 左边没有不同于当前位置字母的字母。那这种情况只能从右边取。同样设右边有R个(不同于当前位置字母的字母),那么可以取2,3,…,R个,有R-1种情况
  3. 右边没有不同于当前位置字母的字母。那这种情况只能从左边取。同样设左边有L个(不同于当前位置字母的字母),那么可以取2,3,…,L个,有L-1种情况

最后把这三种情况加起来就是答案,并且这种方法的时间复杂度为O(n)

举个例子:
y总的图
假设枚举到某一个位置时,当前位置的字母为G,那么想要得到符合条件的结果,只能从其他地方取H

  1. 对于上面的第一种情况,左边可以取1,2,…,L个H,右边可以取1,2,…,R个H,符合乘法原理
  2. 对于第二种情况,只能从右边取H但是至少取两个
  3. 对于第三种情况,之能从左边取,最少两个

附上代码:

#include <iostream>
#include <cstring>
#include <algorithm>using namespace std;typedef long long LL;const int N = 500010;int n;
char str[N];
int l[N], r[N];		//表示左边和右边有多少个与当前位置不一样的连续的字母int main()
{scanf("%d", &n);scanf("%s", str);//先处理左边for (int i = 0, h = 0, g = 0; i < n; i ++ )if (str[i] == 'G') l[i] = h, h = 0, g ++ ;	//当前位置存的G,记录左边扫过的连续的H的数量,连续h的数量就被清空了,开始加g的数量else l[i] = g, g = 0, h ++ ;				//当前位置存的H,记录左边扫过的连续的G的数量,连续g的数量就被清空了,开始加h的数量//再处理右边for (int i = n - 1, h = 0, g = 0; i >= 0; i -- )if (str[i] == 'G') r[i] = h, h = 0, g ++ ;else r[i] = g, g = 0, h ++ ;//所有的情况加起来LL res = 0;for (int i = 0; i < n; i ++ )res += (LL)l[i] * r[i] + max(l[i] - 1, 0) + max(r[i] - 1, 0);printf("%lld\n", res);return 0;
}
//
//作者:yxc
//链接:https://www.acwing.com/activity/content/code/content/5012677/
//来源:AcWing
//著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

这篇关于【AcWing每日一题】4261. 孤独的照片的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/349529

相关文章

【每日一题】LeetCode 2181.合并零之间的节点(链表、模拟)

【每日一题】LeetCode 2181.合并零之间的节点(链表、模拟) 题目描述 给定一个链表,链表中的每个节点代表一个整数。链表中的整数由 0 分隔开,表示不同的区间。链表的开始和结束节点的值都为 0。任务是将每两个相邻的 0 之间的所有节点合并成一个节点,新节点的值为原区间内所有节点值的和。合并后,需要移除所有的 0,并返回修改后的链表头节点。 思路分析 初始化:创建一个虚拟头节点

每日一题|牛客竞赛|四舍五入|字符串+贪心+模拟

每日一题|四舍五入 四舍五入 心有猛虎,细嗅蔷薇。你好朋友,这里是锅巴的C\C++学习笔记,常言道,不积跬步无以至千里,希望有朝一日我们积累的滴水可以击穿顽石。 四舍五入 题目: 牛牛发明了一种新的四舍五入应用于整数,对个位四舍五入,规则如下 12345->12350 12399->12400 输入描述: 输入一个整数n(0<=n<=109 ) 输出描述: 输出一个整数

每日一练7:简写单词(含链接)

1.链接 简写单词_牛客题霸_牛客网 2.题目 3.代码1(错误经验) #include <iostream>#include <string>using namespace std;int main() {string s;string ret;int count = 0;while(cin >> s)for(auto a : s){if(count == 0){if( a <=

【每日刷题】Day113

【每日刷题】Day113 🥕个人主页:开敲🍉 🔥所属专栏:每日刷题🍍 🌼文章目录🌼 1. 91. 解码方法 - 力扣(LeetCode) 2. LCR 098. 不同路径 - 力扣(LeetCode) 3. 63. 不同路径 II - 力扣(LeetCode) 1. 91. 解码方法 - 力扣(LeetCode) //思路:动态规划。 cl

【AcWing】851. 求最短路

spfa算法其实是对贝尔曼福特算法做一个优化。 贝尔曼福特算法会遍历所有边来更新,但是每一次迭代的话我不一定每条边都会更新,SPFA是对这个做优化。 如果说dist[b]在当前这次迭代想变小的话,那么一定是dist[a]变小了,只有a变小了,a的后继(b)才会变小。 用宽搜来做优化,用一个队列,队列里边存的就是所有变小了的结点(队列里存的是待更新的点)。 基本思路就是我更新过谁,我再拿

Windows11电脑上自带的画图软件修改照片大小(不裁剪尺寸的情况下)

针对一张图片,有时候上传的图片有大小限制,那么在这种情况下如何修改其大小呢,在不裁剪尺寸的情况下 步骤如下: 1.选定一张图片,右击->打开方式->画图,如下: 第二步:打开图片后,我们可以看到图片的大小为82.1kb,点击上面工具栏的“重设大小和倾斜”进行调整,如下: 第三步:修改水平和垂直的数字,此处我修改为分别都修改为50,然后保存,可以看到大小变成63.5kb,如下:

想要从OPPO手机恢复数据?免费OPPO照片视频恢复软件

此实用程序可帮助那些寻找以下内容的用户: 在OPPO手机中格式化存储卡后可以恢复图片吗?我删除了 OPPO上的视频和图片,我感觉很糟糕,因为里面有我在拉斯维加斯拍摄的视频和照片 免费OPPO照片视频恢复软件 您能恢复OPPO上已删除的照片吗?我不小心格式化了OPPO SD 卡,有希望恢复已删除的照片吗? 救命!我在清理时删除了我的照片,我的问题是是否有任何免费软件可以从OPPO中恢复已

力扣 739. 每日温度【经典单调栈题目】

1. 题目 理解题意: 1.1. 给一个温度集合, 要返回一个对应长度的结果集合, 这个结果集合里面的元素 i 是 当前 i 位置的元素的下一个更高温度的元素的位置和当前 i 位置的距离之差, 若是当前元素不存在下一个更高温度的元素, 则这个位置用0代替; 2. 思路 本题用单调栈来求解;单调栈就适用于来求当前元素左边或者右边第一个比当前元素大或者小的元素;【单调栈:让栈中的元素保持单调

每日一题——第八十一题

打印如下图案: #include<stdio.h>int main() {int i, j;char ch = 'A';for (i = 1; i < 5; i++, ch++){for (j = 0; j < 5 - i; j++){printf(" ");//控制空格输出}for (j = 1; j < 2 * i; j++)//条件j < 2 * i{printf("%c", ch

每日一题,力扣leetcode Hot100之238.除自身以外数组的乘积

乍一看这个题很简单,但是不能用除法,并且在O(N)时间复杂度完成或许有点难度。 考虑到不能用除法,如果我们要计算输出结果位置i的值,我们就要获取这个位置左边的乘积和右边的乘积,那么我新设立两个数组L和R。 对于L来说,由于表达的是位置i左边的数的乘积,那么L[0]=1,因为第一个数字左边没数那么为了不影响乘积初始值就设置为1,那么L[1]=L[0]*nums[0],那么L[i]=L[i-1