问题描述
一个长度为 n 的字符串 s,其中仅包含 ‘Q’, ‘W’, ‘E’, ‘R’ 四种字符。
如果四种字符在字符串中出现次数均为 n/4,则其为一个平衡字符串。
现可以将 s 中连续的一段子串替换成相同长度的只包含那四个字符的任意字符串,使其变为一个平衡字符串,问替换子串的最小长度?
如果 s 已经平衡则输出0。
Input
一行字符表示给定的字符串s
Output
一个整数表示答案
Sample Input_1
QWER
Sample Output_1
0
Sample Input_2
QQQQ
Sample Output_2
3
解题思路以及关键代码
① l , r 表示左右两个指针,初始化为l = 0, r = -1;
(如果数组是从1开始的,l = 1, r = 0;)
②由于区间内的字母可以随意更改,我们可以先统计区间外的4种字母的数量,然后找到数量最多的字母的数量maxx
③模拟填平操作,total代表区间里面所有元素的个数,free代表填平后剩余的个数
int maxx=max(max(sum1,sum2),max(sum3,sum4));
//填平操作
int total=r-l+1;
int free = total-(maxx-sum1)-(maxx-sum2)-(maxx-sum3)-(maxx-sum4);
④如果剩余个数>=0并且是4的倍数 表示满足条件,此时l++并且维护sum关系,试图去寻找一个更短的连续区间(r++对答案没有贡献,因为可以将r++的那一个元素保持不变)
反之,不满足时r++,试图使当前区间满足条件
if(l <= r && free >=0 && free % 4 == 0)
{
ans = min(ans,total);
if(s[l]=='Q') sum1++;
if(s[l]=='W') sum2++;
if(s[l]=='E') sum3++;
if(s[l]=='R') sum4++;
l++; // [l,r] 符合条件 - l++
}
else
{
r++; // [l,r] 不符合条件 - r++
if(r < n)
{
if(s[r]=='Q') sum1--;
if(s[r]=='W') sum2--;
if(s[r]=='E') sum3--;
if(s[r]=='R') sum4--;
}
}
全部代码
// 1. 答案一定在一个连续区间
// 2. 区间的端点移动有明确方向的
#include<iostream>
#include<string>
using namespace std;
int ans=100000,sum1=0,sum2=0,sum3=0,sum4=0;
string s;
int main()
{
cin>>s;
int n = s.size();
for(int i=0;i<n;i++)
{
if(s[i]=='Q')
sum1++;
if(s[i]=='W')
sum2++;
if(s[i]=='E')
sum3++;
if(s[i]=='R')
sum4++;
}
if(sum1==sum2&&sum2==sum3&&sum3==sum4)
{
cout<<0;
return 0;
}
int l = 0, r = -1;
while(r < n)
{
int maxx=max(max(sum1,sum2),max(sum3,sum4));
//填平操作
int total=r-l+1;
int free = total-(maxx-sum1)-(maxx-sum2)-(maxx-sum3)-(maxx-sum4);
if(l <= r && free >=0 && free % 4 == 0)
{
ans = min(ans,total);
if(s[l]=='Q') sum1++;
if(s[l]=='W') sum2++;
if(s[l]=='E') sum3++;
if(s[l]=='R') sum4++;
l++; // [l,r] 符合条件 - l++
}
else
{
r++; // [l,r] 不符合条件 - r++
if(r < n)
{
if(s[r]=='Q') sum1--;
if(s[r]=='W') sum2--;
if(s[r]=='E') sum3--;
if(s[r]=='R') sum4--;
}
}
}
cout<<ans;
return 0;
}
关于尺取法的总结
什么时候使用尺取法?
所求解的答案为一个连续的区间,并且区间左右端点移动有明确的方向。
经典例题,上述平衡字符串 以及 长度最小的连续区间sum和 >= S(给定常数)