题目描述
难度分:1200
输入T(≤1000)表示T组数据。所有数据的n之和≤2×105,q之和≤2×105。
每组数据输入n(1≤n≤2×105),q(1≤q≤2×105),长为n的字符串s,长为n的字符串t,都只包含小写英文字母。
然后输入q个询问,每个询问输入两个数L和R,表示下标从L到R的子串(1≤L≤R≤n)s[L..R]和t[L..R]。
对于每个询问,你可以修改s[L..R]中的若干字母,使得s[L..R]和t[L..R]排序后相等。输出最小修改次数。
注意:每个询问互相独立。
输入样例
3
5 3
abcde
edcba
1 5
1 4
3 3
4 2
zzde
azbe
1 3
1 4
6 3
uwuwuw
wuwuwu
2 4
1 3
1 6
输出样例
0
1
0
2
2
1
1
0
算法
前缀和
事实上就是在给定L和R的情况下,看看子串s[L…R]和t[L…R]的字母频数差别。操作次数其实就是Σzletter=a|counter1[letter]−counter2[letter]|2,counter1[.]和counter2[.]分别表示.在s[L…R]和t[L…R]中的频数。本质上就是多退少补,s串中相较于t串中少的字母需要通过把相较于t串多的字母改过来。
如此一来,只需要预处理出两个前缀和数组ss和ts即可O(Σ)计算这个求和操作,其中Σ表示字符集大小,本题中Σ=26。ss[c][i]表示字母c在前缀s[1…i]中的出现次数,ts[c][i]表示字母c在前缀t[1…i]中的出现次数。
复杂度分析
时间复杂度
预处理出ss和ts的时间复杂度为O(nΣ)。后续处理每个询问的时间复杂度为O(Σ),处理q个询问的时间复杂度就是O(qΣ)。因此,整个算法的时间复杂度为O((n+q)Σ)。
空间复杂度
额外空间的瓶颈是ss和ts两个前缀和数组,空间复杂度为O(nΣ)。
C++ 代码
#include <bits/stdc++.h>
using namespace std;
const int N = 200010;
char s[N], t[N];
int T, n, q, ss[26][N], ts[26][N];
int main() {
scanf("%d", &T);
while(T--) {
scanf("%d%d", &n, &q);
scanf("%s", s + 1);
scanf("%s", t + 1);
for(char c = 'a'; c <= 'z'; c++) {
for(int i = 1; i <= n; i++) {
ss[c - 'a'][i] = ss[c - 'a'][i - 1] + (s[i] == c? 1: 0);
ts[c - 'a'][i] = ts[c - 'a'][i - 1] + (t[i] == c? 1: 0);
}
}
while(q--) {
int l, r;
scanf("%d%d", &l, &r);
int ans = 0;
for(char c = 'a'; c <= 'z'; c++) {
int i = c - 'a';
ans += abs(ss[i][r] - ss[i][l - 1] - (ts[i][r] - ts[i][l - 1]));
}
ans >>= 1;
printf("%d\n", ans);
}
}
return 0;
}