leetcode
leetcode 751 ~ 800
相似字符串组

相似字符串组

难度:

标签:

题目描述

代码结果

运行时间: 158 ms, 内存: 16.5 MB


/*
 * 思路:
 * 1. 使用流 (Stream) 的方式来简化代码,尤其是在判断相似性和组的合并过程中。
 * 2. 使用并查集(Union-Find)来管理相似组。
 * 3. 最后统计不同的组数。
 */

import java.util.stream.IntStream;

class Solution {
    private int find(int[] parent, int i) {
        if (parent[i] != i) {
            parent[i] = find(parent, parent[i]);
        }
        return parent[i];
    }

    private void union(int[] parent, int[] rank, int i, int j) {
        int rootI = find(parent, i);
        int rootJ = find(parent, j);
        if (rootI != rootJ) {
            if (rank[rootI] > rank[rootJ]) {
                parent[rootJ] = rootI;
            } else if (rank[rootI] < rank[rootJ]) {
                parent[rootI] = rootJ;
            } else {
                parent[rootJ] = rootI;
                rank[rootI]++;
            }
        }
    }

    private boolean areSimilar(String a, String b) {
        return IntStream.range(0, a.length())
            .filter(i -> a.charAt(i) != b.charAt(i))
            .count() <= 2;
    }

    public int numSimilarGroups(String[] strs) {
        int n = strs.length;
        int[] parent = IntStream.range(0, n).toArray();
        int[] rank = new int[n];

        IntStream.range(0, n).forEach(i -> 
            IntStream.range(i + 1, n).filter(j -> areSimilar(strs[i], strs[j]))
            .forEach(j -> union(parent, rank, i, j))
        );

        return (int) IntStream.range(0, n).filter(i -> parent[i] == i).count();
    }
}

解释

方法:

这个题解使用并查集来解决相似字符串分组的问题。主要思路是: 1. 先对输入的字符串数组去重。 2. 根据输入规模选择两种不同的算法: - 当字符串数量n大于字符串长度平方m^2时,使用O(N*M^2)的算法 - 否则使用O(N^2*M)的算法 3. O(N^2*M)算法通过两两比较字符串的相似性,对相似的字符串进行并查集合并操作 4. O(N*M^2)算法先生成每个字符串的所有通配形式,建立通配串到原字符串的映射,同时记录字符串之间的相似关系,最后对有相似关系的字符串进行并查集合并 5. 最后对并查集的根节点去重并输出集合的大小,即为相似字符串组的数量

时间复杂度:

O(min(N^2*M, N*M^2))

空间复杂度:

O(N) ~ O(N*M^2)

代码细节讲解

🦆
在选择使用O(N^2*M)算法或O(N*M^2)算法的依据是什么?为什么当n大于m^2时选择使用O(N*M^2)算法?
选择使用O(N^2*M)或O(N*M^2)算法的依据基于输入规模的不同。当字符串数组的长度n大于字符串长度m的平方时,使用O(N*M^2)算法会更有效率,因为这种情况下每个字符串的变化和比较次数相对较少,可以通过生成所有可能的通配形式来快速识别和关联相似字符串。相比之下,如果n小于或等于m^2,O(N^2*M)算法通过直接比较所有字符串对将更有效,因为此时n较小而m可能较大,直接比较成本较低。
🦆
在并查集中,如何处理并查集的路径压缩以优化查找效率?
并查集的路径压缩是一种优化技术,用于在执行查找操作时减少树的高度,从而提高操作的效率。在查找函数`uni`中,如果当前元素x的父节点不是它自己,即`x != self.p[x]`,则递归调用`uni`函数来找到根节点,并把当前节点的父节点直接设置为根节点。这样,经过几次操作后,树的高度大幅降低,使得后续的查找操作更快。
🦆
题解中提到的`生成每个字符串的所有通配形式`具体是如何实现的?能否具体说明生成规则?
在题解的O(N*M^2)算法中,`生成每个字符串的所有通配形式`是指生成所有可能的两个字符置换后的字符串形式。具体实现方式为:遍历每个字符串,选择两个不同的位置l和r,交换这两个位置的字符后生成一个新的字符串。例如,字符串`abc`在位置1和3交换后得到`cba`。这些通配形式被用于快速检测和记录不同字符串之间的相似关系,通过一个字典存储通配形式到原始字符串的映射,从而在发现两个相同通配形式的字符串时,可以确认它们是相似的。
🦆
为什么相似判定函数`check`在发现两个字符串的不同字符位置超过2个时就返回False?
相似判定函数`check`的目的是判断两个字符串是否通过最多一次字符交换就可以变得相同。如果在比较过程中发现两个字符串在超过两个位置上的字符不同,那么这意味着无法通过单一交换来让这两个字符串相同,因此直接返回False。这是基于题目中对相似字符串的定义,即两个字符串相似当且仅当它们可以通过交换两个字符变得相同。

相关问题