leetcode
leetcode 751 ~ 800
相似度为 K 的字符串

相似度为 K 的字符串

难度:

标签:

题目描述

代码结果

运行时间: 47 ms, 内存: 16.1 MB


/*
 * Problem: Given two anagrams s1 and s2, return the minimum number of swaps required to make s1 equal to s2.
 * Solution: This solution also uses BFS but utilizes Java Streams for some operations.
 */

import java.util.*;
import java.util.stream.Collectors;

public class SolutionStream {
    public int kSimilarity(String s1, String s2) {
        if (s1.equals(s2)) return 0;

        int n = s1.length();
        Queue<State> queue = new LinkedList<>();
        Set<String> visited = new HashSet<>();

        queue.add(new State(s1, 0));
        visited.add(s1);

        while (!queue.isEmpty()) {
            State current = queue.poll();
            List<Integer> mismatches = current.str.chars()
                .filter(c -> c != s2.charAt(current.str.indexOf((char)c)))
                .boxed()
                .collect(Collectors.toList());

            int i = mismatches.isEmpty() ? -1 : mismatches.get(0);
            if (i == -1) continue;

            for (int j = i + 1; j < n; j++) {
                if (current.str.charAt(j) == s2.charAt(j) || current.str.charAt(j) != s2.charAt(i)) continue;

                String next = swap(current.str, i, j);
                if (next.equals(s2)) return current.k + 1;

                if (visited.add(next)) {
                    queue.add(new State(next, current.k + 1));
                }
            }
        }

        return -1;
    }

    private String swap(String s, int i, int j) {
        char[] arr = s.toCharArray();
        char temp = arr[i];
        arr[i] = arr[j];
        arr[j] = temp;
        return new String(arr);
    }

    private static class State {
        String str;
        int k;

        State(String s, int k) {
            this.str = s;
            this.k = k;
        }
    }
}

解释

方法:

此题解采用了深度优先搜索(DFS)的方法来求解字符串s1通过最少次数的交换变为字符串s2的问题。首先,它通过一个循环去除掉s1和s2中相同位置上相同的字符,只保留不同的字符,从而减少了问题的规模。然后,定义一个递归函数dfs来尝试所有可能的字符交换,通过递归搜索找到最小的交换次数。递归过程中使用了剪枝来减少不必要的计算,例如,如果当前已有的交换次数已经超过了已知的最小交换次数,就停止进一步搜索。此外,还计算了当前不匹配的字符对至少需要的交换次数来进行更深入的剪枝。

时间复杂度:

O(n!)

空间复杂度:

O(n)

代码细节讲解

🦆
在递归函数`dfs`中,为什么可以直接返回0当`n == 0`,即没有不同的字符?是否意味着s1和s2完全相同?
在`dfs`函数中,当`n == 0`时可以直接返回0,是因为`n`代表的是s1和s2中位置相同但字符不同的字符对的数量。如果`n`为0,这意味着没有这样的字符对,即s1和s2在所有相同位置上的字符完全相同。因此,不需要任何交换操作,s1已经和s2完全相同了。
🦆
在寻找可以与`s[i]`交换的字符`s[j]`时,为什么仅考虑`s[j] == t[i]`的情况?是否存在其他可能的有效交换条件?
在选择进行交换的字符时,考虑`s[j] == t[i]`的情况是因为这样的交换能直接解决位置`i`的不匹配问题,即将位置`i`的字符直接变为目标字符`t[i]`。虽然理论上可以考虑其他交换以期望通过多步骤达到整体最优,但这通常会导致问题复杂度急剧增加,并且难以保证得到最小交换次数。因此,题解中主要考虑这种可以直接减少不匹配数量的有效交换。
🦆
题解中提到的剪枝策略`如果当前成本已经超过已知最小值,则剪枝`是如何具体实现的?能否详细解释这种剪枝的逻辑?
这种剪枝策略的逻辑是基于当前搜索路径的成本。如果在`dfs`递归过程中,当前路径上已经累积的交换次数(成本)超过了之前找到的最小交换次数,那么继续当前路径的搜索只会得到更大的交换次数。因此,可以立即停止当前路径的进一步搜索,这样做能有效减少不必要的计算,提高算法的效率。
🦆
题解估算最少还需要的交换次数时使用了`(diff + 1) // 2`,这个计算公式是如何得出的?它依据的是什么原理?
计算公式`(diff + 1) // 2`用于估算至少还需要的交换次数,其中`diff`是当前不匹配的字符对的数量。这个公式基于一个简单的假设:每一次有效的交换至少可以解决一个字符对的不匹配。因此,在最理想的情况下,每两个不匹配的字符通过一次交换可以被修正(考虑到交换是两个字符间的操作)。所以,`diff`个不匹配的字符对至少需要`diff / 2`次交换,而`(diff + 1) // 2`是`diff / 2`向上取整的结果,确保覆盖所有奇数情况。

相关问题

情侣牵手

n 对情侣坐在连续排列的 2n 个座位上,想要牵到对方的手。

人和座位由一个整数数组 row 表示,其中 row[i] 是坐在第 i 个座位上的人的 ID。情侣们按顺序编号,第一对是 (0, 1),第二对是 (2, 3),以此类推,最后一对是 (2n-2, 2n-1)

返回 最少交换座位的次数,以便每对情侣可以并肩坐在一起每次交换可选择任意两人,让他们站起来交换座位。

 

示例 1:

输入: row = [0,2,1,3]
输出: 1
解释: 只需要交换row[1]和row[2]的位置即可。

示例 2:

输入: row = [3,2,0,1]
输出: 0
解释: 无需交换座位,所有的情侣都已经可以手牵手了。

 

提示:

  • 2n == row.length
  • 2 <= n <= 30
  • n 是偶数
  • 0 <= row[i] < 2n
  • row 中所有元素均无重复