Red Red Red!!!
题目背景
Red(Yes); Purple(No).
题目描述
小红拿到了一个长度为 n 的字符串,该字符串仅由大写字母组成。
她想取一个子串,该子串包含至少 a 个 'R' 字符,至多 b 个 'P' 字符。
你能告诉她有多少合法的方案可以取到吗?
注意:
1.子串定义见 英文wiki-substring 或 中文wiki-子串
例如:"an" 是 "ant" 的子串而 "at" 不是。
2.只要子串的起始位置或终止位置不同,我们就认为是两个不同的方案。
输入格式
第一行三个正整数 n, a 和 b ,用空格隔开。
第二行一行字符串,该字符串保证仅包含大写字母('A'到'Z')。
数据范围:
输出格式
取一个子串,包含至少 a 个 'R' 字符,至多 b 个 'P' 字符的方案数。
输入输出样例
输入 #1
13 3 0 RRRPBRRRDBRPR输出 #1
10输入 #2
5 1 1 RARPR输出 #2
13
说明/提示
样例一解释:
共有 10 个合法的子串选择方式。假设下标是从 0 到 12 ,那么 10 个合法串分别是:
s[0,2] = "RRR"
s[4,7] = "BRRR"
s[4,8] = "BRRRD"
s[4,9] = "BRRRDB"
s[4,10] = "BRRRDBR"
s[5,7] = "RRR"
s[5,8] = "RRRD"
s[5,9] = "RRRDB"
s[5,10] = "RRRDBR"
s[6,10] = "RRDBR"
这些串均含有至少 3 个字符 'R',且含有至多 0 个字符 'P'
题解:预处理前缀和,双指针,第二个指针动的时候要二分(可以直接upper_bound)
#include<bits/stdc++.h>
using namespace std;
#define ll long long
const int mo = 1e3;
ll ans = 0;
string s;
int cntr = 0, cntp = 0;
int n, a, b, rr[2000200], pp[2000200];
int main()
{
ios::sync_with_stdio(false);
cin >> n >> a >> b;
cin >> s;
for (int i = 0; i < n; i++)
{
if (s[i] == 'R')
{
cntr++;
}
rr[i + 1] = cntr;
if (s[i] == 'P')
{
cntp++;
}
pp[i + 1] = cntp;
}
int j = 1;
for (int i = 1; i <= n; i++)
{
while (rr[j] - rr[i - 1] < a && j < n )
{
j++;
}
int flg = j;
// cout<<"ij= "<<i<<" "<<j<<endl;
if (rr[j] - rr[i - 1] >= a)
{
int k = upper_bound(pp + 1, pp + n+1, pp[i - 1] + b) - pp-1; //j的位置
// cout<<"k= "<<k<<endl;
if (k > n)k = n;
int an = k - flg + 1;
an=max(an,0);
ans+=an;
/* 这样做会超时,所以要二分(upper_bound)一下
while (pp[j] - pp[i - 1] <= b && j <= n)
{
ans++;
j++;
}*/
}
j = max(flg, i + 1);
}
cout << ans;
return 0;
}