21级大数据拔尖班练习赛【周次:4】 - Virtual Judge
汉明距离表示两个等长字符串在对应位置上不同字符的数目,举例来说 "0011" 和 "0110" 中汉明距离为 |0 - 0| + |0 - 1| + |1 - 1| + |1 - 0| = 0 + 1 + 0 + 1 = 2。我们以d(x, y)d(x,y)表示字符串xx和yy之间的汉明距离,现在给出两个仅包含01的字符串x,yx,y,同时定义s(A,B)s(A,B)代表字符串AA中所有长度为|B|的子串,请你求出xx和s(y,x)s(y,x)的汉明距离的总和,也就是∑d(x,s(y,x))∑d(x,s(y,x))。
输入1
01
0011
输出1
2
输入2
01
00111
输出2
3
思路
我们先取到x和y的长度:首先考虑x(01)的第一位(0),它只与y(0011)的前三位比较,如果y为0对答案没有贡献,只有y为1才对答案贡献1;再考虑x的第二位(1),它从y的第一位开始比较,一直到最后一位,只用y为0时才对答案贡献1,y为1则不贡献答案
于是我们看到二十万的数据,肯定不能使用双指针遍历两次,这样会得到n方的时间复杂度会狠狠超时,则很容易想到使用前缀和求这一段区间内0和1的出现次数
#include <iostream>
#include <cstring>
#include <algorithm>
typedef long long LL;
using namespace std;
const int N = 400010;
LL a[N],s[N];
string x,y;
int main(){
cin>>x>>y;
LL lenx=x.size(), leny=y.size();
for(LL i=0 ; i<leny ; i++) a[i]=y[i]-'0'; //将每个1、0字符转化为int类型整数
LL sum=0;
for(LL i=0 ; i<leny ; i++){
s[i]=s[i-1]+a[i]; //预处理前缀和
}
LL re=0;
//我们只需考虑每位数字对答案的贡献
for(LL i=0 ; i<lenx ; i++){
if(x[i] == '0') re+=s[leny-lenx+i]-s[i-1];
else if(x[i] == '1') re+=leny-lenx+1-(s[leny-lenx+i]-s[i-1]);
}
cout<<re;
return 0;
}