codeforce1196D1、D2 RGB Substring

问题链接(https://codeforces.com/problemset/problem/1196/D1)
问题链接(https://codeforces.com/problemset/problem/1196/D2)

问题描述

有长为n的字符串,要求通过修改一些字符得到一个长度为k的子串,使得这个子串是"RGBRGB…"的子串。输出修改的字符的最小个数。D1与D2的区别是n,k的大小不一样。

问题分析

D1做法:暴力遍历。因为规模较小,所以通过选择不同开头(所给字符的开头和“RGB”的开头),计数来得到每个情况得到的k长子串与无限长度"RGB"的相同位置上字符相同的个数,越大越好。最后取最大的与k相减就是答案。值得注意的是,"RGB…"的开头只有3种。
D2做法:前缀和降维。由于规模变大了,之前的做法会超时。但我们可以发现,我们重复计算了很多东西,尤其是一个个求每个子串的字符相同的个数。这些重复的计算可以通过用前缀和处理掉。
由于"RGB…“的子串只有三种开头,所以匹配的时候要分成3种情况,分别以’R’、‘G’、'B’开头的”…RGB…"对原串的匹配,也就是下表的1、2、3原串的错位对齐。由于原串的开头也要变化,所以先求{x1,x2,x3,…,xk}与“RGB…”的字符匹配个数,按D1的做法接着是求{x2,x3,x4,…,x(k+1)}与“RGB…”的匹配个数,可以发现,这个问题其实在情况3的时候会再处理一次,所以为了减少重复的计算,在情况1就不进行以x2、R开头的计算了,相同的其它这样的问题都会在情况1、2、3中穿插,进行一次就好了。其次是,利用前缀和快速找到(即不进行过多的中间计算)每个情况中每个长度为k的子串的匹配情况,虽然也是求和,但这一步就是减少时间花费的最重要一步。

RGBRGBRG
1x1x2x3x4x5x6x7x8
2x1x2x3x4x5x6x7
3x1x2x3x4x5x6

代码如下

//D1
#include<bits/stdc++.h>
using namespace std;

const string rgb="RGB";

int main(){
	ios::sync_with_stdio(false),cin.tie(0),cout.tie(0);
	string str;
	int q,i,j,k,n,t,ti,tj,maxn,temp;
	cin>>q;
	while(q--){
		cin>>n>>k>>str;
		maxn=0;
		for(i=0;i<3;i++){
			for(j=0;j<n;j++){
				if(j+k>n) break;
				temp=0;
				ti=i,tj=j;//设置开头
				for(t=0;t<k;t++){
					if(ti==3) ti=0;
					if(rgb[ti++]==str[tj++]) temp++;
				}
				maxn=max(maxn,temp);
			}
		}
		cout<<k-maxn<<endl;
	}
	return 0;
}
//D2
#include<bits/stdc++.h>
using namespace std;

const int N=2e5+5; 
const string rgb="RGB";

int sum[N];
char s[N];

int main(){
	string str;
	int q,i,j,k,n,t,ti,tj,maxn,temp;
	scanf("%d",&q);
	while(q--){
		scanf("%d %d %s",&n,&k,s);
		str=s;
		maxn=0;
		for(i=0;i<3;i++){
			sum[0]=0;
			ti=i;
			for(j=0;j<n;j++,ti++) sum[j+1]=sum[j]+(rgb[ti%3]==str[j]?1:0);//得到前缀和数组
			int m=n-k+1;//j+k>n+1 break
			for(j=0;j<=m;j++) maxn=max(maxn,sum[j+k]-sum[j]);//遍历找到字符最大匹配数
		}
		printf("%d\n",k-maxn);
	}
	return 0;
}
#endif 
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值