D1.D2. RGB Substring (easy version,hard version)(前缀和+贪心) Codeforces Round #575 (Div. 3)

原题链接: https://codeforces.com/problemset/problem/1196/D2
在这里插入图片描述
测试样例:

Input
3
5 2
BGGGG
5 3
RBRGR
5 5
BBBRR
Output
1
0
3

Note:

In the first example, you can change the first character to ‘R’ and obtain the substring “RG”, or change the second character to ‘R’ and obtain “BR”, or change the third, fourth or fifth character to ‘B’ and obtain “GB”.

In the second example, the substring is “BRG”.

题意: 你有一个无限长的序列“RGBRGBRGB······”,给你一个长为n的字符串序列s,你需要找到一个长为k属于字符串s的子字符串,同时这个字符串也应该是这个无限长序列的子串。当然,如果你找不到,你可以更改字符串s,将任意一位上的字符更改,问你最少需要更改的次数。

解题思路: 这道题我是以D2的级别去做的,当然不会暴力解决。我们的思路是什么?我们暂且来看看我们是想要得到最少需要更改的次数,如果我以这个无限长序列直接和给定字符串进行对比,判断哪一个位置上的字符串需要更改,那么我们默认字符串上每连续k位都能构造成子字符串满足条件,只不过需要进行我们统计的改写操作。那么这里我们可以使用前缀和来完成节省时间效率,那么我们不就是寻找最少的更改次数了吗?遍历连续的k寻找最小值。这里还需注意的一点是循环序列对于字符串s来说不一定是RGB开头的,也可能是GBR,也可能是BRG,这三种情况我们都需要判断取最小值,同时我们也没必要去构建一个无限长的序列,而是根据n的大小来构建一个长度大于等于n这样的循环序列。OK,经过以上思路分析和注意事项,相信你能写出代码来的,若还是不太清楚,请看AC代码,附详细注释。

AC代码:

/*
*邮箱:unique_powerhouse@qq.com
*blog:https://me.csdn.net/hzf0701
*注:文章若有任何问题请私信我或评论区留言,谢谢支持。
*
*/
#include<bits/stdc++.h>	//POJ不支持

#define rep(i,a,n) for (int i=a;i<=n;i++)//i为循环变量,a为初始值,n为界限值,递增
#define per(i,a,n) for (int i=a;i>=n;i--)//i为循环变量, a为初始值,n为界限值,递减。
#define pb push_back
#define IOS ios::sync_with_stdio(false);cin.tie(0); cout.tie(0)
#define fi first
#define se second
#define mp make_pair

using namespace std;

const int inf = 0x3f3f3f3f;//无穷大
const int maxn = 2e5+2;//最大值。
typedef long long ll;
typedef long double ld;
typedef pair<ll, ll>  pll;
typedef pair<int, int> pii;
//*******************************分割线,以上为自定义代码模板***************************************//

int q,n,k;
string s;
int pre[maxn],diff[maxn];//为全局变量,元素都被自动初始化为0,这是小技巧,diff记录哪里的位置不同。pre[i]记录前i个元素不同的个数。
int solve(string temp){
	while(temp.size()<n)temp+=temp;//构造一个长大于等于n的字符串供对比出哪里不同。
	//为了让其下标为1开始,因为我们的前缀和。
	temp="#"+temp;
	rep(i,1,n){
		if(temp[i]!=s[i]){
			diff[i]=1;
		}
		else{
			diff[i]=0;
		}
		pre[i]=pre[i-1]+diff[i];
	}
	//当退出循环后则已构建好,我们则可以遍历选择最小更改次数了。
	int ans=inf;
	rep(i,k,n){
		ans=min(ans,pre[i]-pre[i-k]);
	}
	return ans;
}
int main(){
	//freopen("in.txt", "r", stdin);//提交的时候要注释掉
	IOS;
	while(cin>>q){
		while(q--){
			cin>>n>>k;
			cin>>s;
			s="#"+s;//让s下标从1开始。
			//这个无穷循环序列可能有三种形式:RGBRGB---  GBRGBR--- BRGBRG--。我们这里都要判断取最小值。
			int ans=solve("RGB");
			ans=min(ans,solve("GBR"));
			ans=min(ans,solve("BRG"));
			cout<<ans<<endl;
		}
	}
	return 0;
}

posted @   unique_pursuit  阅读(22)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 全程不用写代码,我用AI程序员写了一个飞机大战
· DeepSeek 开源周回顾「GitHub 热点速览」
· 记一次.NET内存居高不下排查解决与启示
· 物流快递公司核心技术能力-地址解析分单基础技术分享
· .NET 10首个预览版发布:重大改进与新特性概览!
点击右上角即可分享
微信分享提示