LeetCode 839 相似字符串组(并查集解题模板)

一月的力扣每日一题似乎是并查集月,在一月的最后一天,总结一下并查集的解题模板吧。在解决图论相关问题的时候,如果我们不关心图的路径如何连接,而只关心图的连通性和连通分量的个数,我们可以考虑采用并查集来解决。
并查集最重要的两个操作是查找和合并操作。同时,为了提高并查集的合并和查找效率,在查找的时候我们可以执行路径压缩,在合并的时候我们可以采取按秩合并的方案。
接下来我总结了并查集这个数据结构的标准模板,包括了合并与查找操作。

class UnionFind {
public:
    vector<int> parent; //存储父节点
    vector<int> size;  //存储当前节点所在连通分量的节点合数,便于按秩合并操作
    int n;
    // 当前连通分量数目
    int setCount;
    
public: //构造函数,执行初始化操作
    UnionFind(int _n): n(_n), setCount(_n), parent(_n), size(_n, 1) {
        iota(parent.begin(), parent.end(), 0);
    }
    
    int findset(int x) { //寻找根节点,并执行了路径压缩算法
        return parent[x] == x ? x : parent[x] = findset(parent[x]);
    }
    
    bool unite(int x, int y) {  //连接两个节点所在连通分量
        x = findset(x); //x的父节点
        y = findset(y); //y的父节点
        if (x == y) {  //在同一个连通分量中
            return false;
        }
        if (size[x] < size[y]) {  //按秩合并的判断,x所在连通分量元素小于y
            swap(x, y);
        }
        parent[y] = x; //把y所在联通分量的根节点设置为x
        size[x] += size[y];  //同时x所在联通分量的规模变成两者之和
        --setCount;  //联通分量个数-1
        return true;
    }
    
    bool connected(int x, int y) { //判断是否在同一个连通分量之中
        x = findset(x);
        y = findset(y);
        return x == y;
    }
};

以上就是并查集的标准模板,根据模板,我们来解决1.31号的每日一题,题目是hard级别,但是其实只要熟悉并查集,基本是秒杀。
题干如下:
在这里插入图片描述
读完题目,我们可以发现,如果把每个字符串当做一个节点,把两个字符串是否相似当做两个节点是否有边,这就是一个图论问题。其实题目要求的就是图的连通分量个数,可以采用并查集解答。
解答如下:

class Solution {
public:  //解决方案:并查集
    vector<int> root;

    //寻找当前连通分量的根节点,并且采用路径压缩优化连通分量
    int findRoot(int x){
        return root[x] == x ? x : root[x] = findRoot(root[x]);
    }

    //判断两个字符串是否相似
    bool check(string& s1, string& s2, int len){
        int num = 0; //统计不同字符的个数
        for(int i = 0;i<len;i++){
            if(s1[i] != s2[i]){
                num++;
            }
            if(num > 2){
                return false;
            }
        }
        return true;
    }

    int numSimilarGroups(vector<string>& strs) {
        int n = strs.size();  //字符串数量
        int len = strs[0].size();  //一个字符串长度
        root.resize(n); //给向量分配n个元素空间
        for(int i = 0;i<n;i++){
            root[i] = i;
        }
        for(int i = 0;i<n;i++){
            for(int j = i+1;j<n;j++){
                int r1 = findRoot(i);  //寻找根节点
                int r2 = findRoot(j);
                if(r1 == r2){  //已经在同一个连通分量里,直接跳过
                    continue;
                }
                if(check(strs[i],strs[j],len)){  //判断是否相似,相似则合并
                    root[r1] = r2; //r1,r2交换位置无所谓
                }
            }
        }
        int res = 0;
        for(int i = 0;i<n;i++){ //统计连通分量的个数
            if(root[i] == i){
                res++;
            }
        }
        return res;
    }
};
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值