You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

66 lines
3.8 KiB

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

#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;
}