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;
}
posted @ 2021-02-06 11:32  pbc的成长之路  阅读(152)  评论(0编辑  收藏  举报