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

2 years ago
#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,xy:
Sp[],,p[0]={2,4} 'a'2,4
Ss[],,s[10][0]=3 S10'a'3
ab,ab使
yx,vector<int> f[][][]:
26x
26y
:,....
x
f[][][]kkyx
*/
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;
}