题目描述
难度分:1500
输入长度均≤2×105的字符串s和t,只包含0和1。并且t的长度大于等于s的长度。
定义D(a,b)=|a[0]−b[0]|+|a[1]−b[1]|+…+|a[n−1]−b[n−1]|。
例如D(0011
,0110
)=|0−0|+|0−1|+|1−1|+|1−0|=0+1+0+1=2。
设s的长度为n,对于t的所有长为n的连续子串t′,计算D(s,t′)。
输出所有D(s,t′)的和。
输入样例1
01
00111
输出样例1
3
输入样例2
0011
0110
输出样例2
2
算法
贡献法
先预处理出前缀和数组presum,presum[0][i]表示前缀s[1…i]中0的数量,presum[1][i]表示前缀s[1…i]中1的数量。
然后遍历i∈[1,m]计算每个t[i]对答案的贡献,对于i位置,当s在t上滑窗时,t[i]可以对应到s串的位置是[l,r]=[max(1,i−m+n),min(i,n)],分为以下两种情况:
- 当t[i]=
0
时,它的贡献为presum[1][r]−presum[1][l−1],因为只有和s中的字符不同时才会产生贡献。 - 当t[i]=
1
时,它的贡献为presum[0][r]−presum[0][l−1]。
复杂度分析
时间复杂度
遍历一遍s串,预处理出前缀和数组presum,再遍历一遍t串求得答案。因此,算法整体的时间复杂度为O(n+m)。
空间复杂度
空间消耗就在于前缀和数组presum,关于0和1的前缀和数组各占O(n)的空间,算法整体的额外空间复杂度为O(n)。
C++ 代码
#include <bits/stdc++.h>
using namespace std;
const int N = 200010;
char s[N], t[N];
int presum[2][N];
int main() {
scanf("%s", s + 1);
scanf("%s", t + 1);
int n = strlen(s + 1), m = strlen(t + 1);
for(int i = 1; i <= n; i++) {
presum[0][i] = presum[0][i - 1] + (s[i] == '0'? 1: 0);
presum[1][i] = presum[1][i - 1] + (s[i] == '1'? 1: 0);
}
function<int(int , int, int)> windowSum = [&](int tp, int l, int r) {
return presum[tp][r] - presum[tp][l - 1];
};
long long ans = 0;
for(int i = 1; i <= m; i++) {
int d = t[i] - '0';
ans += windowSum(1 - d, max(1, i - m + n), min(i, n));
}
printf("%lld\n", ans);
return 0;
}