题目描述
一个长度为 n 的字符串 s,其中仅包含 ‘Q’, ‘W’, ‘E’, ‘R’ 四种字符。
如果四种字符在字符串中出现次数均为 n/4,则其为一个平衡字符串。
现可以将 s 中连续的一段子串替换成相同长度的只包含那四个字符的任意字符串,使其变为一个平衡字符串,问替换子串的最小长度?
如果 s 已经平衡则输出0。
1<=n<=105,n是4的倍数,字符串中仅包含字符 ‘Q’, ‘W’, ‘E’ 和 ‘R’.
Input
一行字符表示给定的字符串s
Output
一个整数表示答案
Examples
input
QWER
output
0
input
QQWE
output
1
input
QQQW
output
2
input
QQQQ
output
3
算法/思路分析
本题问满足替换后为平衡字符串的最小连续替换子串的长度。
考虑到所求的为一个连续区间且区间左右端点移动有明确方向,因此可使用尺取法/双指针法,具体而言:[l,r]满足要求,l++,继续判断;[l,r]不满足要求,r++,再判断,由此即可遍历所有连续区间。
判断是否满足要求:首先算出不含[l,r]的字符串内四个字母的出现次数,记录四个数中最大值MAX,将[l,r]中部分元素替换,从而使四个字母的出现次数均为MAX,之后计算[l,r]中替换后的剩余元素数量FREE是否为4的倍数。若是4的倍数且大于0,则可以作为连续替换子串,否则不行。
代码
#include <cstdio>
#include <iostream>
#include <stack>
#include <algorithm>
#include <cmath>
#include <string>
using namespace std;
const int maxn = 1e9+10;
#define ll long long
string s;
int max(int a,int b,int c,int d)
{
int ans1 = max(a,b);
int ans2 = max(c,d);
return max(ans1,ans2);
}
int main()
{
cin >> s;
//记录字符串中四个字母的出现次数
int sum1,sum2,sum3,sum4;
sum1 = sum2 = sum3 = sum4 = 0;
int length = s.length();
for(int i = 0; i < length; i++)
{
if(s[i] == 'Q') sum1++;
else if(s[i] == 'W') sum2++;
else if(s[i] == 'E') sum3++;
else if(s[i] == 'R') sum4++;
}
//四个字母出现次数相同即为平衡字符串,直接输出0
if(sum1 == sum2 && sum2 == sum3 && sum3 == sum4)
{
printf("0\n");
return 0;
}
//非平衡字符串,使用双指针l,r
int l = 0,r = 0,ans = maxn;
while(l <= r && r < length)
{//记录[l,r]之外的字符串中四个字母的出现次数
int sum11 = sum1,sum22 = sum2,sum33 = sum3,sum44=sum4;
for(int i = l; i < r+1; i++)
{
if(s[i] == 'Q') sum11--;
else if(s[i] == 'W') sum22--;
else if(s[i] =='E') sum33--;
else if(s[i] == 'R') sum44--;
}
//通过替换,使四类字母出现次数一致
int MAX = max(sum11,sum22,sum33,sum44);
int TOTAL = r - l + 1;
//判断剩余空闲位置是否为4的倍数,对则满足条件,l++,否则r++
int MAX_S = (MAX - sum11) + (MAX - sum22) + (MAX - sum33) + (MAX - sum44);
int FREE = TOTAL - MAX_S;
if(FREE >= 0 && FREE % 4 == 0)
{
ans = min(ans,TOTAL);
if(l == r)
{
l++;
r++;
}
else l++;
}
else r++;
}
printf("%d\n",ans);
return 0;
}
/*
QWER
QQWE
QQQW
QQQQ
*/