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*的方式来优化搜索空间,启发式搜索
什么意思呢 ?
我们知道,当前字符串到目标字符串的最小交换次数,肯定和两个字符串之间不同的字符串相关
那么我们就可以贪心的思想,去改进搜索顺序,我们设计一个估价函数
估价函数如何设计呢? 这个比较套路,可以直接设计为 两个字符串之间不同的字符
代码
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;
}
};