题意:
一个长度为 n 的字符串 s,其中仅包含 ‘Q’, ‘W’, ‘E’, ‘R’ 四种字符。如果四种字符在字符串中出现次数均为 n/4,则其为一个平衡字符串。现可以将 s 中连续的一段子串替换成相同长度的只包含那四个字符的任意字符串,使其变为一个平衡字符串,问替换子串的最小长度?
如果 s 已经平衡则输出0。
Input
一行字符表示给定的字符串s
Output
一个整数表示答案
Examples
Input
QWER
Output
0
Input
QQWE
Output
1
Input
QQQW
Output
2
Input
QQQQ
Output
3
思路:
对于这个字符串中的某个区间来说,如何判断这个区间能不能通过改变其中的一些元素的值使得整个字符串平衡,我们可以首先通过遍历整个字符串,算出整个字符串中四种元素各有多少,然后我们可以再次遍历这个区间算出这个区间内四种元素有多少,总共有多少位,这些总共的位数其实就是这个区间可以改变值的最大的位数,通过整个字符串某字符的个数 - 这个区间内的字符的个数 = 区间外的字符的个数,区间外四个字符各自的数目,这时候采取填平操作,即将区间外字符数少的字符通过改变区间内的元素,达到外部平衡,这时候只要再内部平衡即可,即除去改变的字符位数以外,剩下的字符位数是4的倍数。这样就满足平衡,这个区间符合要求。
如何得到最小的区间呢,对于刚刚所说的区间假设为[l, r],这个区间已经满足要求,我们希望得到更小的区间,那么必然是要将左端点右移,右端点不动来进行判断,若是这个区间不可,那么则将右端点右移,构成新的区间。
这便满足了尺取法的要求:
求解答案为一个区间;
区间左右端点移动有明确的方向
注意若是我们已经得到了一个ans即区间长度,那么对于对于区间长度实际上已经大于ans的区间而言,这个区间对题目没有帮助,因此可以直接将左端点右移至区间长度小于ans的情况,这样可以少讨论一些情况。
总结:
尺取法是维护所取区间的左右端点作为两个指针,然后根据实际情况不断地推进区间左右端点以得出答案。尺取法是一种高效的枚举区间的方法,一般用于求取有一定限制的区间个数或最短的区间等等。
代码:
#include<iostream>
#include<string>
#include<algorithm>
using namespace std;
char m[100000];
void getnumber(int& numberQ, int& numberW, int& numberE, int& numberR, int l, int r)
{
for (int i = l; i <= r; i++)
{
if (m[i] == 'Q')numberQ++;
if (m[i] == 'W')numberW++;
if (m[i] == 'E')numberE++;
if (m[i] == 'R')numberR++;
}
}
int main()
{
cin >> m;
int numberQ = 0, numberW = 0, numberE = 0, numberR = 0;
int length = 0;
while (m[length] != '\0')
{
if (m[length] == 'Q')numberQ++;
if (m[length] == 'W')numberW++;
if (m[length] == 'E')numberE++;
if (m[length] == 'R')numberR++;
length++;
}
if (numberE == numberQ && numberE == numberR && numberE == numberW)
cout << 0 << endl;
else
{
int ans = length;
int l = 0, r = 0;
while (r <= length - 1)
{
int small_Q = 0, small_W = 0, small_E = 0, small_R = 0;
getnumber(small_Q, small_W, small_E, small_R, l, r);
int sum_Q = numberQ - small_Q; //找到除了[l,r]以内的Q还有多少个
int sum_W = numberW - small_W;
int sum_E = numberE - small_E;
int sum_R = numberR - small_R;
int maxx = max(max(sum_Q, sum_W), max(sum_E, sum_R));
int total = r - l + 1;//总共可以更改的位数
total -= (4 * maxx - sum_Q - sum_E - sum_W - sum_R); //用total把少于max的全部填起来,看剩下的还有多少个
if (total >= 0 && total % 4 == 0)
{
if (ans > (r - l + 1))ans = r - l + 1;
if (l == r) //注意当区间左右端点一致时需要同时将左右位置+1
{
l++, r++;
}
else
l++;
}
else
{
r++;
if (ans < r - l + 1)
l = r - ans;
}
}
cout << ans << endl;
}
}