1. 问题背景与核心思路
这道题目来自AtCoder Beginner Contest 441的E题,要求我们统计字符串中满足特定条件的子串数量。具体来说,我们需要找出所有子串中'A'字符数量严格大于'B'字符数量的情况。
1.1 问题重述
给定一个由'A'、'B'、'C'组成的字符串S,长度为n。我们需要计算所有满足条件的子串S[i..j]的数量,其中子串中'A'的数量严格大于'B'的数量。
1.2 核心思路解析
这道题的关键在于如何高效地统计满足条件的子串数量。直接暴力枚举所有子串并检查条件的时间复杂度是O(n²),对于较大的n(比如1e5)显然不可行。我们需要找到一种线性时间复杂度的解法。
核心思路是使用前缀和配合哈希表来优化统计过程:
- 定义前缀和数组pre,其中pre[i]表示前i个字符中'A'的数量减去'B'的数量
- 对于任意子串S[i..j],其中'A'的数量减去'B'的数量等于pre[j]-pre[i-1]
- 我们需要统计所有i<j且pre[j]-pre[i-1]>0的情况
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 算法优化与实现细节
2.1 前缀和偏移技巧
由于前缀和可能出现负值,直接作为数组下标不方便。常见的处理方法是进行偏移:
cpp复制vector<int>tr(2*n+5,0); // 哈希表数组
int cnt = n; // 当前前缀和(已偏移)
这里我们使用2n+5大小的数组tr来记录各个前缀和出现的次数。初始时cnt=n,相当于把原始前缀和0映射到n的位置。
2.2 实时统计有效子串
在遍历字符串时,我们维护以下变量:
- cnt:当前前缀和(已偏移)
- o:以当前位置结尾的有效子串数量
- ans:累计的总有效子串数
对于每个字符的处理逻辑:
- 遇到'A':前缀和增加1,有效子串数增加tr[cnt-1]
- 遇到'B':前缀和减少1,有效子串数减少tr[cnt]
- 遇到'C':不做任何操作
每次处理后,更新tr[cnt]并累加o到ans中。
2.3 关键代码解析
cpp复制for(int i=0;i<n;i++){
if(s[i]=='A'){
cnt++;
o+=tr[cnt-1];
}e
