题意
汉明距离:给定两个长度相等的字符串(a)和(b),汉明距离(H(a, b) = sum_{i = 1}^{|a|}(a_i e 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 e b_i)且(s_i = a_i),则(t = 1)
-
若(a_i e 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 e 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, ' ', 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
", cas, ans);
}
return 0;
}