【2022-09-21每日一题】854. 相似度为 K 的字符串[Hard]
2022-09-21
4分钟阅读时长
2022-09-21每日一题:854. 相似度为 K 的字符串
难度:Hard
标签:广度优先搜索 、 字符串
对于某些非负整数 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
的一个字母异位词
方法一:广度优先搜索
详细思路过程见官方题解,这里只做个人刷题记录,方便后续查询阅读
func kSimilarity(s1 string, s2 string) (step int) {
type pair struct {
s string
i int
}
q := []pair{{s1, 0}}
vis := map[string]bool{s1: true} // 避免重复搜索
for n := len(s1); ; step++ {
tmp := q
q = nil
for _, p := range tmp {
s, i := p.s, p.i
if s == s2 { // 搜索终止,返回最少次数
return step
}
// 跳过相同字母
for i < n && s[i] == s2[i] {
i++
}
t := []byte(s)
for j := i+1; j < n; j++ {
// 找相同字母,且对应位置字母不同
if s[j] == s2[i] && s[j] != s2[j] {
t[i], t[j] = t[j], t[i]
if t := string(t); !vis[t] {
vis[t] = true
q = append(q, pair{t, i+1})
}
// 恢复
t[i], t[j] = t[j], t[i]
}
}
}
}
return step
}
方法二:深度优先搜索
func kSimilarity(s1 string, s2 string) int {
var s, t []byte
// 搜索不同的位置
for i := 0; i < len(s1); i++ {
if s1[i] != s2[i] {
s, t = append(s, s1[i]), append(t, s2[i])
}
}
// s1 == s2 情况,直接返回
n := len(s)
if n == 0 {
return 0
}
minSwap := func(i int) int {
diff := 0
for j := i; j < n; j++ {
if s[j] != t[j] {
diff++
}
}
return (diff+1) >> 1 // 相当于除2
}
ans := n - 1
var dfs func (int, int)
dfs = func(i, cost int) {
// 剪枝
if cost >= ans {
return
}
// 跳过相同位置
for i < n && s[i] == t[i] {
i++
}
if i == n {
ans = min(ans, cost)
return
}
// 当前状态的交换次数下限大于等于当前的最小交换次数
if cost+minSwap(i) >= ans {
return
}
for j := i+1; j < n; j++ {
// todo: 此处官方没有 s[j] != t[j], 可以移除
if s[j] == t[i] && s[j] != t[j] {
s[i], s[j] = s[j], s[i]
dfs(i+1, cost+1)
s[i], s[j] = s[j], s[i]
}
}
}
dfs(0, 0)
return ans
}
func min(a, b int) int {
if a < b {
return a
}
return b
}
方法三:A* 启发式搜索
func kSimilarity(s1 string, s2 string) int {
n := len(s1)
pq := PriorityQueue{}
heap.Push(&pq, State{0, 0, 0, s1})
visit := map[string]bool{s1: true}
for pq.Len() > 0 {
state := heap.Pop(&pq).(State)
cost, pos, s := state.cost, state.pos, state.s
if s == s2 {
return state.cost
}
for pos < n && s[pos] == s2[pos] {
pos++
}
t := []byte(s)
for j := pos+1; j < n; j++ {
if s[j] == s2[pos] && s[j] != s2[j] {
t[pos], t[j] = t[j], t[pos]
if t := string(t); !visit[t] {
visit[t] = true
heap.Push(&pq, State{cost+1+minSwap(s2, t, pos+1), cost+1, pos+1, t})
}
t[pos], t[j] = t[j], t[pos]
}
}
}
return 0
}
func minSwap(s1, s2 string, i int) int {
diff := 0
for j := i; j < len(s1); j++ {
if s1[j] != s2[j] {
diff++
}
}
return (diff+1)>>1
}
type State struct {
priority, cost, pos int
s string
}
type PriorityQueue []State
func (p PriorityQueue) Len() int {
return len(p)
}
func (p PriorityQueue) Less(i, j int) bool {
if p[i].priority != p[j].priority {
return p[i].priority < p[j].priority
} else {
if p[i].cost != p[j].cost {
return p[i].cost < p[j].cost
} else {
return p[i].pos < p[j].pos
}
}
}
func (p PriorityQueue) Swap(i, j int) {
p[i], p[j] = p[j], p[i]
}
func (p *PriorityQueue) Push(v interface{}) {
*p = append(*p, v.(State))
}
func (p *PriorityQueue) Pop() (v interface{}) {
v, *p = (*p)[p.Len()-1], (*p)[:p.Len()-1]
return v
}
官方C++
class Solution {
public:
int minSwap(const string &s1, const string &s2, const int &pos) {
int tot = 0;
for (int i = pos; i < s1.size(); i++) {
tot += s1[i] != s2[i];
}
return (tot + 1) / 2;
}
int kSimilarity(string s1, string s2) {
typedef tuple<int, int, int, string> State;
int n = s1.size();
priority_queue<State, vector<State>, greater<State>> pq;
unordered_set<string> visit;
pq.emplace(0, 0, 0, s1);
visit.emplace(s1);
while (!pq.empty()) {
auto [_, cost, pos, cur] = pq.top();
pq.pop();
if (cur == s2) {
return cost;
}
while (pos < n && cur[pos] == s2[pos]) {
pos++;
}
for (int j = pos + 1; j < n; j++) {
if (s2[j] == cur[j]) {
continue;
}
if (s2[pos] == cur[j]) {
swap(cur[pos], cur[j]);
if (!visit.count(cur)) {
visit.emplace(cur);
pq.emplace(cost + 1 + minSwap(s2, cur, pos + 1), cost + 1, pos + 1, cur);
}
swap(cur[pos], cur[j]);
}
}
}
return 0;
}
};
方法四:动态规划
参考官方题解:https://leetcode.cn/problems/k-similar-strings/solution/xiang-si-du-wei-k-de-zi-fu-chuan-by-leet-8z10/