codeforces 1196D2 RGB Substring (hard version)

codeforces 1196D2 RGB Substring (hard version) 题目链接

难版和简易版唯一的区别是数据的范围,简易版可以直接暴力,难版需要用DP(动态规划)

题目:

You are given a string s consisting of n characters, each character is ‘R’, ‘G’ or ‘B’.

You are also given an integer k. Your task is to change the minimum number of characters in the initial string s so that after the changes there will be a string of length k that is a substring of s, and is also a substring of the infinite string “RGBRGBRGB …”.

A string a is a substring of string b if there exists a positive integer i such that a1=bi, a2=bi+1, a3=bi+2, …, a|a|=bi+|a|−1. For example, strings “GBRG”, “B”, “BR” are substrings of the infinite string “RGBRGBRGB …” while “GR”, “RGR” and “GGG” are not.

You have to answer q independent queries.

Input
The first line of the input contains one integer q (1≤q≤2⋅105) — the number of queries. Then q queries follow.

The first line of the query contains two integers n and k (1≤k≤n≤2⋅105) — the length of the string s and the length of the substring.

The second line of the query contains a string s consisting of n characters ‘R’, ‘G’ and ‘B’.

It is guaranteed that the sum of n over all queries does not exceed 2⋅105 (∑n≤2⋅105).

Output
For each query print one integer — the minimum number of characters you need to change in the initial string s so that after changing there will be a substring of length k in s that is also a substring of the infinite string “RGBRGBRGB …”.

输入样例:

3
5 2
BGGGG
5 3
RBRGR
5 5
BBBRR

输出样例:

1
0
3

先给出代码:

#include<bits/stdc++.h>
using namespace std;
int q,n,k;
string s;
char jh[3]={'R','G','B'};	//比对时用
int f[200005];
int main(){
    cin>>q;
    for(int i=0;i<q;i++){
    	cin>>n>>k;
    	cin>>s;
    	int ans=300000;      //赋个极大值,遇到比ans小的值就更新ans
		for(int j=0;j<3;j++){   //枚举开头可能为三种颜色,因开头可能也需要变
			int flag=j;			//用于记录RGB的顺序
			int cnt=0;
			for(int x=0;x<n;x++){	//按照RGB的顺序一个一个比对
				f[x]=(s[x]!=jh[flag]);	//记录每一位是否需要变化,0为不用1为用
				if(f[x]){
					cnt+=f[x];	//记录总消费
				}
				flag++;
				flag%=3;
				if(x>=k){	//到达k位时已选取k+1个,需要每次减去最前面的消耗
					cnt-=f[x-k];	//需在更新前,防止多算一位的消耗
				}
				if(x>=k-1){
					ans=min(ans,cnt);//遇到比ans小的值就更新ans
									 //前提是已经到k-1位,这样已经选取了k个数了
				}
			}
		}
    	cout<<ans<<endl;
	}
	return 0;
}

总体思路:

首先,我们的思路是在n个字符中,挪动k长度的区间,通过变量记录更新最小值
当然,我们需要先确定RGB的顺序,可以是RGBRGB…也可以是GBRGBR…或者BRGBRG…我们无法确定哪个更优,消耗更小,因此枚举三种可能的顺序
用数组记录每一位是否符合我们枚举的顺序,并将其累加,当长度到达k时,便可以不断更新我们的ans值了,取最小的,当然长度为k+1时需要减去最前面的那位的消耗,因为区间类似于在整体移动,往后走了一位,前面就要减去一位,因为题目要求的是找一个连续区间长度为k的符合RGB顺序的区间,更改正确所需要的最小消耗
这样时间就为3*n,时间复杂度就只有O(n)

你可能感兴趣的:(算法,枚举)