题目
题意
一个长度为 n 的字符串 s,其中仅包含 ‘Q’, ‘W’, ‘E’, ‘R’ 四种字符。
如果四种字符在字符串中出现次数均为 n/4,则其为一个平衡字符串。
现可以将 s 中连续的一段子串替换成相同长度的只包含那四个字符的任意字符串,使其变为一个平衡字符串,问替换子串的最小长度?
如果 s 已经平衡则输出0。
Input
一行字符表示给定的字符串s
Output
一个整数表示答案
Note
1<=n<=10^5
n是4的倍数
字符串中仅包含字符 ‘Q’, ‘W’, ‘E’ 和 ‘R’.
题目大意
本题会给出一个仅有 “Q”、“W”、“E”、“R” 包含的字符串,且长度一定位4的倍数。如果四个字母数量不相等,则修改其中某一子串,使得修改后的字符串满足要求,问需要修改的最短子串长度为多少。无需修改则输出0。
例如s = “QQWE” 转变为 “RQWE”,答案为 1。
解题思路
本题需要使用尺取法,首先来看尺取法的使用条件:
• 所求解答案为一个连续区间
• 区间左右端点移动有明确方向
因此对于本题来说,需要修改的是一段连续子串,且可以从左到右移动左右端点来寻找对应的最小子串,所以可以使用尺取法。
在使用尺取法求最小区间时,首先要注意左右端点的移动。首先将左右端点都置为1,随后开始判断区间 [ l , r ] 是否满足要求。当对应区间 [ l , r ] 满足题目要求,说明这是以 l 端点开头的最小长度,所以不必继续移动右端点,此事更新最小长度 r - l + 1 ,随后将左端点 l 加一继续判定 [ l , r ] ;如果区间不满足条件,说明还需要继续扩长区间,所以将右端 r 点加一继续判定。这样一直持续到左右端点 l 和 r 都达到 n ,于是结束循环,得出最小区间长度。这是尺取法必有的一般操作。
针对不同的题目,判断区间 [ l , r ] 是否满足要求是关键所在。本题对于区间 [ l , r ] ,首先得到区间外四个字母的数量 sum1 ~ sum4 ,得到四者中的最大值 maxi ,之后用 free 表示区间长度 r - l + 1 减去 maxi 与 sum1 ~ sum4 的差值,这个操作完成后可以理解为修改完成,此时只要 free 大于 0 并且能整除 4 就说明该段满足要求。上述的算式如下:
MAX = max(sum1, sum2, sum3, sum4)
TOTAL = R – L + 1
FREE = TOTAL [(MAX-sum1)+(MAX-sum2)+(MAX-sum3)+(MAX-sum4)]
综上所述,按照尺取法框架和判断区间是否合适的方法,最终得出最小区间长度即可。
具体代码
#include<iostream>
#include<algorithm>
#include<cmath>
#define ll long long
#define ld long double
using namespace std;
int code[200005];
int n, sums[5];
int main()
{
int l = 0, r = 0;
int ans = 1e9,n;
string temp;
cin >> temp;
n = temp.length();
for(int i = 0; i < n; i++)
{
if(temp[i] == 'Q')
{
code[i] = 1;
}
else if(temp[i] == 'W')
{
code[i] = 2;
}
else if(temp[i] == 'E')
{
code[i] = 3;
}
else if(temp[i] == 'R')
{
code[i] = 4;
}
sums[code[i]]++;
}
while(l < n && r < n)
{
int sum[5];
for(int i = 1; i <= 4; i++)
{
sum[i] = sums[i];
}
for(int i = l; i <= r; i++)
{
sum[code[i]]--;
}
int maxi = max(max(sum[1],sum[2]),max(sum[3],sum[4]));
int total = r - l + 1;
int free = total - (maxi - sum[1]) - (maxi - sum[2]) - (maxi - sum[3]) - (maxi - sum[4]);
if(free >= 0 && free % 4 == 0)
{
ans = min(ans,total);
l++;
}
else
{
r++;
}
}
cout << ans << endl;
return 0;
}