当前位置 博文首页 > Inmaturity_7的博客:算法练习帖--59--相似字符串组(Java)

    Inmaturity_7的博客:算法练习帖--59--相似字符串组(Java)

    作者:[db:作者] 时间:2021-07-16 21:51

    相似字符串组

    一、题目简介

    如果交换字符串 X 中的两个不同位置的字母,使得它和字符串 Y 相等,那么称 X 和 Y 两个字符串相似。如果这两个字符串本身是相等的,那它们也是相似的。

    例如,“tars” 和 “rats” 是相似的 (交换 0 与 2 的位置); “rats” 和 “arts” 也是相似的,但是 “star” 不与 “tars”,“rats”,或 “arts” 相似。

    总之,它们通过相似性形成了两个关联组:{“tars”, “rats”, “arts”} 和 {“star”}。注意,“tars” 和 “arts” 是在同一组中,即使它们并不相似。形式上,对每个组而言,要确定一个单词在组中,只需要这个词和该组中至少一个单词相似。

    给你一个字符串列表 strs。列表中的每个字符串都是 strs 中其它所有字符串的一个字母异位词。请问 strs 中有多少个相似字符串组?
    (题目来源:力扣(LeetCode))

    示例 1:
    输入:strs = ["tars","rats","arts","star"]
    输出:2
    
    示例 2:
    输入:strs = ["omv","ovm"]
    输出:1
    
    提示:
    1 <= strs.length <= 100
    1 <= strs[i].length <= 1000
    sum(strs[i].length) <= 2 * 104
    strs[i] 只包含小写字母。
    strs 中的所有单词都具有相同的长度,且是彼此的字母异位词。
    
    备注:
          字母异位词(anagram),一种把某个字符串的字母的位置(顺序)加以改换所形成的新词。
    

    二、解决方法

    1. 并查集

    class UnionFind{
            int[] parent;//父结点数组
            int[] rank;//秩数组
            int count;//统计相似字符串组数量
    
            /**
             * 初始化方法
             * @param length
             */
            public UnionFind(int length) {
                parent=new int[length];
                rank=new int[length];
                count=length;
                for (int i = 0; i < length; i++) {
                    parent[i]=i;
                }
            }
    
            /**
             * 寻找当前结点的父结点
             * @param index
             * @return
             */
            public int find(int index){
                while(index!=parent[index]){
                    parent[index]=parent[parent[index]];
                    index=parent[index];
                }
                return index;
            }
    
            /**
             * 合并集合方法
             * @param index1
             * @param index2
             */
            public void union(int index1,int index2){
                int root1=find(index1);
                int root2=find(index2);
                if(root1==root2){
                    return;
                }
                if(rank[root1]>rank[root2]){
                    parent[root2]=root1;
                }else if(rank[root1]<rank[root2]){
                    parent[root1]=root2;
                }else{
                    parent[root2]=root1;
                    rank[root1]++;
                }
                count--;
            }
        }
    
    
        public int numSimilarGroups(String[] strs) {
            //创建并查集对象
            UnionFind nsg = new UnionFind(strs.length);
            //获取字符串的长度(所有的字符串长度一样)
            int charL=strs[0].length();
            for (int i = 1; i < strs.length; i++) {
                for (int j = 0; j < i; j++) {
                    //判断当前字符串是否隶属于前面的相似字符串组数组
                    //是的话就合并
                    if(checkStr(strs[i],strs[j],charL)){
                        nsg.union(i,j);
                    }
                }
            }
            return nsg.count;
        }
    
        /**
         * 判断是否可以合并相似字符串组
         * @param str1 字符串1      
         * @param str2 字符串2
         * @param charLength 字符串长度
         * @return
         */
        private boolean checkStr(String str1,String str2,int charLength) {
            //记录两个字符串相同下标存储的字符不相同的数量
            int diffNum=0;
            for (int i = 0; i < charLength; i++) {
                if(str1.charAt(i)!=str2.charAt(i)){
                    diffNum++;
                    if(diffNum>2){
                        //大于两个不同字符就直接返回false
                        return false;
                    }
                }
            }
            return true;
        }
    

    2. 并查集(官方题解)

    class Solution {
        int[] f;//parent数组
    
        public int numSimilarGroups(String[] strs) {
            int n = strs.length;
            int m = strs[0].length();
            f = new int[n];
            //初始化parent数组
            for (int i = 0; i < n; i++) {
                f[i] = i;
            }
            for (int i = 0; i < n; i++) {
                for (int j = i + 1; j < n; j++) {
                	//找到两个字符串结点的父结点
                    int fi = find(i), fj = find(j);
                    if (fi == fj) {//父结点相同,直接查找下一个
                        continue;
                    }
                    if (check(strs[i], strs[j], m)) {
                    	//合并相似字符串组
                        f[fi] = fj;
                    }
                }
            }
            //计算相似字符串组数量
            int ret = 0;
            for (int i = 0; i < n; i++) {
                if (f[i] == i) {
                    ret++;
                }
            }
            return ret;
        }
    	 /**
         * 寻找当前结点的父结点
          * @param x
          * @return
          */
        public int find(int x) {
            return f[x] == x ? x : (f[x] = find(f[x]));
        }
         /**
         * 判断是否可以合并相似字符串组
         * @param str1 字符串1
         * @param str2 字符串2
         * @param charLength 字符串长度
         * @return
         */
        public boolean check(String a, String b, int len) {
            //记录两个字符串相同下标存储的字符不相同的数量
            int num = 0;
            for (int i = 0; i < len; i++) {
                if (a.charAt(i) != b.charAt(i)) {
                    num++;
                    if (num > 2) {
                    	//大于两个不同字符就直接返回false
                        return false;
                    }
                }
            }
            return true;
        }
    }
    
    cs