Files
python/TangDou/JiShuanKe/2023-03-10/T3668_1.cpp
HuangHai 1f397eca87 'commit'
2025-08-30 18:35:01 +08:00

66 lines
3.8 KiB
C++
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#include <bits/stdc++.h>
using namespace std;
const int N = 2e5 + 10;
int n, m; // n个长度的原串,m个询问
char S[N]; // 原串
vector<int> p[26]; // 记录每个字符出现的位置,比如'a'出现在位置1,3,5,...
vector<int> f[26][26]; // 三维数组,从谁,二维:到谁,三维:第几次出现,值:有多少个从谁
int s[N][26]; // 分类前缀和
// 使用STL版本的lower_bound,upper_bound
int main() {
freopen("ridge.in", "r", stdin);
freopen("ridge.out", "w", stdout);
cin >> n >> m >> (S + 1);
/* (1)因为题目数据范围很大,只能用O(NlogN)的时间复杂度(或更低)才能过掉,所以在输入数据时,必须千方百计的预处理提高性能
(2)从最后询问的问题来思考,设原串为S,开始字符为x、结束字符为y:
① 预处理出S中每个字符出现的位置p[],在询问时可以只枚举有用的位置,例:p[0]={2,4} 表示'a'出现在2,4两个位置。
② 预处理出S中每个字符出现的次数s[],可以用前缀和,例:s[10][0]=3 表示在S的前10个字符中,'a'字符出现了3次。
只有上面两个预处理出的数组还不行,因为串中可能多次出现a和b,但有些a在b后面,直接使用①②是不对的。
③ 预处理出:每当y出现时,记录前面已经出现过了多少个x,用数组vector<int> f[][][]来表示:
第一维代表是26个可能的来源字符x
第二维代表是26个可能的终止字符y
第三维代表是:第一次出现,第二次出现,....
值:x之前出现次数
④ 以f[][][]为基础数据,再次三层循环累加起来的值,表示:第k次出现时,前k个y与前面所有x的配对数量,是一个累加前缀和概念。
*/
for (int i = 1; i <= n; i++) {
int y = S[i] - 'a'; // 当前字符y
for (int x = 0; x < 26; x++) {
s[i][x] = s[i - 1][x];
f[x][y].push_back(s[i][x]); // x是肯定有的,每回26个,y是因为看到了当前的字符y。换句话说:就是y出来一次,就push_back了26个x的统计数据
}
p[y].push_back(i); // 字符y出现在i这个位置上
s[i][y]++; // 前i个字符中,字符y出现的次数增加了1个
}
// 计算区间前缀和
for (int i = 0; i < 26; i++) // 从字符i
for (int j = 0; j < 26; j++) // 到字符j
for (int k = 1; k < (int)f[i][j].size(); k++) // 有多条记录
f[i][j][k] += f[i][j][k - 1]; // 生成前缀和
while (m--) {
int l, r;
string t;
cin >> l >> r >> t;
int x = t[0] - 'a', y = t[1] - 'a';
int ql = lower_bound(p[y].begin(), p[y].end(), l) - p[y].begin(); // y的左边界
int qr = upper_bound(p[y].begin(), p[y].end(), r) - p[y].begin() - 1; // y的右边界
if (ql > qr) { // 如果没有找到y,输出0
cout << 0 << endl;
continue;
}
int cnt = qr - ql + 1; // y的个数
// 两层前缀和
// 以y的右边界结尾,计算y_右与前面所有x的配对数量=f[x][y][qr]
// 以y的左边界结尾,计算y_右与前面所有x的配对数量=f[x][y][ql - 1]
// 两者的差,还需要继续减去前l-1个中存在的x与[l,r]区间内的y的配对关系数量
// 需要注意的是:因为使用的是vector,下标从0开始,如果直接使用ql-1可能会有下标为负数的风险,需要判断一下
cout << f[x][y][qr] - (ql == 0 ? 0 : f[x][y][ql - 1]) - s[l - 1][x] * cnt << endl;
}
return 0;
}