Hamming Distance(贪心,函数,构造)
题意
汉明距离:给定两个长度相等的字符串\(a\)和\(b\),汉明距离\(H(a, b) = \sum_{i = 1}^{|a|}(a_i \ne b_i)\)
现在给定两个长度相等、全由小写字母构成的字符串\(a\)和\(b\)。求字典序最小的字符串\(s\),使得\(H(a, s) = H(b, s)\)
数据范围
\(1 \leq T \leq 100\)
\(1 \leq |a| \leq 10000\)
思路
定义函数\(f(x) = H(a, x) - H(b, x)\),我们的目标是找一个\(s\),使得\(f(s) = 0\)
我们可以看一下\(x\)的每一位对函数值的贡献,不妨设可以使函数值增加\(t\)。
-
若\(a_i \ne b_i\)且\(s_i = a_i\),则\(t = 1\)
-
若\(a_i \ne b_i\)且\(s_i = b_i\),则\(t = -1\)
-
其他情况,\(t = 0\)
因此,可以得出结果,通过调整\(x\),可以使得\(f(x)\)连续变化。
为了保证字典序最小,我们要尽可能的选择较小的字符,但是存在一个问题就是我前面选了较小的字符,但是后面无论怎么选都无法满足条件了。
为了应对上述情况,我们采用一个预处理。就是统计字符串后缀可以使得\(f(x)\)产生多大的变化范围。因此,定义一个\(suf[i]\)数组,表示的是\(i \sim |a|\)范围内\(f(x)\)的变化幅度。
即,统计\(i \sim |a|\)范围内,\(a_i \ne b_i\)的个数。因此,在第\(i\)位从小到大枚举每个字符,如果当前的字符满足\(|f(x_i)| \leq suf_{i + 1}\),那么就可以选这个字符。
代码
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
const int N = 10010;
char s1[N], s2[N];
char ans[N];
int suf[N];
bool solve(char ch, int idx, int &dif)
{
int tmp = dif + (ch != s1[idx]) - (ch != s2[idx]);
if(abs(tmp) <= suf[idx + 1]) {
dif = tmp;
ans[idx] = ch;
return true;
}
return false;
}
int main()
{
int T;
scanf("%d", &T);
for(int cas = 1; cas <= T; cas ++) {
scanf("%s%s", s1, s2);
memset(suf, 0, sizeof(suf));
memset(ans, '\0', sizeof(ans));
int len = strlen(s1);
for(int i = len - 1; i >= 0; i --) {
suf[i] = suf[i + 1];
if(s1[i] != s2[i]) suf[i] ++;
}
int dif = 0;
for(int i = 0; i < len; i ++) {
for(int j = 0; j < 26; j ++) {
if(solve(j + 'a', i, dif)) break;
}
}
printf("Case %d: %s\n", cas, ans);
}
return 0;
}