[每日一题] 854. 相似度为 K 的字符串

77 阅读1分钟

854. 相似度为 K 的字符串

本文已参与「新人创作礼」活动,一起开启掘金创作之路。

对于某些非负整数 k ,如果交换 s1 中两个字母的位置恰好 k 次,能够使结果字符串等于 s2 ,则认为字符串 s1 和 s2 的 相似度为 k ;

给你两个字母异位词 s1 和 s2 ,返回 s1 和 s2 的相似度 k 的最小值。

示例 1:

输入: s1 = "ab", s2 = "ba"
输出: 1

示例 2:

输入: s1 = "abc", s2 = "bca"
输出: 2

提示:

1 <= s1.length <= 20
s2.length == s1.length
s1 和 s2  只包含集合 {'a', 'b', 'c', 'd', 'e', 'f'} 中的小写字母
s2 是 s1 的一个字母异位词

思路

题目保证一定有解,从多个解中求最小解

对于这个题目,交互两位,然后达到目标串

那么我们的搜索空间还是比较大的,若是字符串更小一点,我们就可以直接暴力bfs or dfs 来搜索解空间

为什么可以这样,(要不去复习一下经典问题 八数码 ?)

超时如何解决!

一种可行的办法是 采用剪枝来操作他们

有那些方法剪枝呢?

A*

IDA*

DFS + 剪枝

我们采用 A*的方式来优化搜索空间,启发式搜索

什么意思呢 ?

我们知道,当前字符串到目标字符串的最小交换次数,肯定和两个字符串之间不同的字符串相关

那么我们就可以贪心的思想,去改进搜索顺序,我们设计一个估价函数

我们设当前走到x的真实距离为g(x)我们设当前x走到s2的真实距离为h(x),估计距离为h(x)真实距离f(x)=g(x)+h(x)估计距离f(x)=g(x)+h(x)对于我们的估计距离是满足h(x)h(x)所以,当每次从队列中取出最小的f(x)来拓展的时候都会去更新相邻点 我们设 当前走到x的真实距离为 g(x) \\ 我们设 当前x走到s2的 真实距离为 h(x), 估计距离为 h^*(x) \\ 真实距离 f(x) = g(x) + h(x) \\ 估计距离 f^*(x) = g(x) + h^*(x) \\ 对于我们的估计距离 是 满足 h(x) \leq h^*(x)的 \\ 所以,当每次从队列中取出最小的 f^*(x) 来拓展的时候 \\ 都会去更新相邻点

估价函数如何设计呢? 这个比较套路,可以直接设计为 两个字符串之间不同的字符 cnt/2\lceil cnt / 2 \rceil

代码

class Solution {
public:
    int kSimilarity(string s1, string s2) {
        unordered_map<string, int> mp;
        mp[s1] = 0;
        auto $ = [](const string& a, const string& b) -> int {
            int cnt = 0;
            assert(a.size() == b.size());
            for (int i = 0; i < a.size(); i ++) cnt += a[i] != b[i];
            return (cnt + 1) / 2;
        };
        auto cmp = [&](string a, string b) -> bool{
            return (mp[a] + $(a, s2)) > (mp[b] + $(b, s2));
        };
        priority_queue<string, vector<string>, decltype(cmp)> que(cmp);
        que.push(s1); 
        while (que.size()) {
            auto t = que.top(); que.pop();
            if (t == s2) return mp[t];
            string tm = t;
            for (int i = 0; i < t.size(); i ++) {
                if (t[i] == s2[i]) continue;
                for (int j = i + 1; j < t.size(); j ++) {
                    if (tm[j] == s2[j]) continue;
                    swap(tm[i], tm[j]);
                    if (!mp.count(tm) || mp[tm] > mp[t] + 1) {
                        mp[tm] = mp[t] + 1;
                        que.push(tm);
                    }
                    swap(tm[i], tm[j]);
                }
                break;
            }
        }
        return 114514;
    }
};