# Eye of the Storm ### 题目描述 云浅来到了风暴的中心。这里漂浮着一个长为 $n$ 的,由小写字母组成的字符串 $S$。**字符串的下标从 $1$ 开始。** 想要逃出风暴,就需要回答一些询问。 每次询问会给出一对正整数 $l,r$ 和一个字符串 $T$,云浅需要回答 $S_{l\cdots r}$ 这段子串内有多少个子序列是 $T$。**这里保证 $T$ 的长度为 $2$。** 形式化地,你需要求出有多少对 $(i,j)$ 满足 $l\le i ### 题解 记字符集大小为 $V=26$。 考虑设 $\text{Sum}(i,c)$ 为 $S_{1\cdots i}$ 中字符 $c$ 的出现次数,那么答案就是$$\displaystyle \sum_{l\le i\le r,S_i==T_2}\text{Sum}(i-1,T_1)-\text{Sum}(l-1,T_1) $$用一个 `vector` 来存一下 $S$ 中等于 $T_2$ 的数的出现位置,顺便附带记录一下 $\text{Sum}$,每次查询的时候二分出 $r$ 前面和 $l$ 后面的第一个满足等于 $S_i==T_2$ 的 $i$ 的位置,那么要求的就是 `vector` 内的一个前缀和。具体可以看代码。 时间复杂度 $O(nV+q\log n)$,空间复杂度 $O(nV)$。 #### 参考代码 ```c++{.line-numbers} #include #define int long long using namespace std; inline int read() { int x = 0, f = 1; char c = getchar(); for (; (c < '0' || c > '9'); c = getchar()) { if (c == '-') { f = -1; } } for (; (c >= '0'&& c <= '9'); c = getchar()) { x = x * 10 + (c& 15); } return x * f; } int n, q; string S, T; #define mk make_pair #define fi first #define se second const int MN = 2e5 + 5; vector pos[26], sum[26][26]; int f[MN][26]; signed main(void) { freopen("ridge.in", "r", stdin); freopen("ridge.out", "w", stdout); cin >> n >> q; cin >> S; for (int i = 1; i <= n; i++) { int t = (int)(S[i - 1] - 'a'); for (int x = 0; x < 26; x++) { f[i][x] = f[i - 1][x], sum[t][x].push_back(f[i][x]); } pos[t].push_back(i); f[i][t]++; } for (int t = 0; t < 26; t++) { for (int x = 0; x < 26; x++) { for (int j = 1; j < sum[t][x].size(); j++) { sum[t][x][j] += sum[t][x][j - 1]; } } } while (q--) { int l, r; cin >> l >> r >> T; if (l > r) { puts("**"); return 0; } int a = T[0] - 'a', b = T[1] - 'a'; int ql = lower_bound(pos[b].begin(), pos[b].end(), l) - pos[b].begin(); int qr = upper_bound(pos[b].begin(), pos[b].end(), r) - pos[b].begin() - 1; if (ql > qr) { puts("0"); continue; } int cnt = qr - ql + 1; cout << sum[b][a][qr] - (ql == 0 ? 0 : sum[b][a][ql - 1]) - f[l - 1][a] * cnt << endl; } return 0; } ```