贡献法是一种思想,就是去思考每一个答案能对最终的答案贡献出多少。
在本题中,我们可以不去枚举能够选择出多少种孤独照片,而是转而去找出所有的孤独的牛然后思考每个牛能够贡献出多少不同的区间,这就是贡献法在本题中的体现。
按照题目要求,找孤独照片的数目就是去找在3或3头以上牛组成的序列中仅包含一个不同种族的牛的区间,所以我们可以直接在字符串中找到左右连续的H中间夹杂着一个G的序列或是左右连续的G的中间夹杂着一个H的序列。
在找到之后,假设以这头不同种族的牛为中心,它的左边的连续其他种族的牛的数量为L,右边的为R。
那么一定可以仅通过计算得出能够组成的区间数。
- 在左边至少有一头并且右边至少有一头的情况下:能够组成的不同区间数量就是 L ∗ R L*R L∗R
- 在左边至少有两头并且右边没有的情况下:能够组成的不同区间数量就是 L − 1 L-1 L−1
- 在左边没有并且右边至少有两头的情况下:能够组成的不同区间数量就是 R − 1 R-1 R−1
这时候注意学到这一点:不要在原数组上进行困难的计数操作,直接先预处理出来每个字符所在位置对应的左边连续另一种族的数量和右边连续另一种族的数量。
全部预处理出来之后,就可以直接枚举每一个牛然后直接加上对应的左右计算得来的值。
代码:
#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
const int N = 5e5+10;
int l[N]; //l和r代表着每一个下标左右两边连续的不同类型的字符的数量
int r[N];
int main(){
int n;cin >> n;
string str;cin >> str;
for(int i = 0 , cntG = 0 , cntH = 0;i < n;i++){ //用cntH来对连续H计数,cntG对连续G计数
if(str[i] == 'G')l[i] = cntH,cntG++,cntH = 0;//如果遇到了G就说明断掉了H的连续,所以加上连续H的数量然后将其重置,同时让G的计数加一
else l[i] = cntG,cntH++,cntG = 0; //如果遇到了H就说明断掉了G的连续,以下同理
}
for(int i = n-1 , cntG = 0 , cntH = 0;i >= 0;i--){
if(str[i] == 'G') r[i] = cntH,cntG++,cntH = 0;
else r[i] = cntG,cntH++,cntG = 0;
}
long long ans = 0;
for(int i = 0;i < n;i++){//直接枚举所有牛,把所有牛当作孤独的牛然后加上预处理后的l和r的计算结果
ans += (long long)l[i]*r[i] + max(l[i]-1,0) + max(r[i] - 1,0);
}
cout << ans;
return 0;
}