Leetcode_graph 839. 相似字符串组 (dfs/union_set求连通分量)

839. 相似字符串组

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

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

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

我们给出了一个不包含重复的字符串列表 A。列表中的每个字符串都是 A 中其它所有字符串的一个字母异位词。请问 A 中有多少个相似字符串组?

 

示例:

输入:["tars","rats","arts","star"]
输出:2
提示:

A.length <= 2000
A[i].length <= 1000
A.length * A[i].length <= 20000
A 中的所有单词都只包含小写字母。
A 中的所有单词都具有相同的长度,且是彼此的字母异位词。
此问题的判断限制时间已经延长。
备注:
      字母异位词[anagram],一种把某个字符串的字母的位置(顺序)加以改换所形成的新词。

 58.88% 50.00%

方法1. 不用图

1、相似的string在一个set内, 这样L会慢慢减少,减少的L进行下一轮。
2、用list因为其删除/增加的效率高。
3、 L.erase(j++)必须这样写,不然会出错。因为修改list会影响iterator。

class Solution {
private:
    bool _isValid(const string &x, const string &y) {
        int len = x.size();
        char diff = 0;

        for (int i = 0; i < len; ++i) {
            if (x[i] != y[i])
                diff++;
            if (diff > 2)
                return false;
        }
        return true;
    }

    int _oneSet(list<string> &L, const int &step) {
        int size = L.size();
        list<string> tmp;
        tmp.push_back(L.front());
        L.pop_front();

        for (auto i = tmp.begin(); i != tmp.end(); ++i) {
            for (auto j = L.begin(); j != L.end();) {
                if (_isValid(*i, *j)) {
                    tmp.push_back(*j);
                    L.erase(j++);
                } else {
                    j++;
                }
            }
        }

        if (L.empty()) {
            return step;
        } else {
            return _oneSet(L, step + 1);
        }
    } 
public:
    int numSimilarGroups(vector<string>& A) {
        list<string> L(A.begin(), A.end());
        return _oneSet(L, 1);
    }
};

方法2. union-set 并查集             O(N^2 W) 建图

class Solution {
public:
    int numSimilarGroups(vector<string>& A) {
        int res = 0, n = A.size();
        vector<int> root(n);
        for (int i = 0; i < n; ++i) root[i] = i;
        for (int i = 1; i < n; ++i) {
            for (int j = 0; j < i; ++j) {
                if (!isSimilar(A[i], A[j])) continue;
                root[getRoot(root, j)] = i;
            }
        }
        for (int i = 0; i < n; ++i) {
        	if (root[i] == i) ++res;
        }
        return res;
    }
    int getRoot(vector<int>& root, int i) {
        return (root[i] == i) ? i : getRoot(root, root[i]);
    }
    bool isSimilar(string& str1, string& str2) {
   		for (int i = 0, cnt = 0; i < str1.size(); ++i) {
   			if (str1[i] == str2[i]) continue;
   			if (++cnt > 2) return false;
   		}
   		return true;
   	}
};

 方法3. 先构建图 O(N^2 W),然后深度遍历图去计算有几个连通子树

class Solution {
public:
    bool isSim(const string& s1, const string& s2) {
        if (s1.size() != s2.size()) return false;
        int diff = 0;
        for (int i = 0; i < s1.size(); ++i) {
            if (s1[i] != s2[i]) {
                ++diff;
                if (diff > 2) return false;
            }
        }
        return true;
    }
    void dfs(vector<vector<int> >& g, int i, vector<bool>& seen) {
        seen[i] = true;
        for (auto j : g[i]) {
            if (!seen[j]) {
                dfs(g, j, seen);
            }
        }
    }
    int numSimilarGroups(vector<string>& A) {
        int N = A.size();
        vector<vector<int > > g(N);
        for (int i = 0; i < N; ++i) {
            g[i].push_back(i);
            for (int j = i + 1; j < N; ++j) {
                if (isSim(A[i], A[j])) {
                    g[i].push_back(j);
                    g[j].push_back(i);
                }
            }
        }
        vector<bool> seen(N, false);
        int res = 0;
        for (int i = 0; i < N; ++i) {
            if (!seen[i]) {
                ++res;
                dfs(g, i, seen);
            }
        }
        return res;
    }
};


发布了88 篇原创文章 · 获赞 77 · 访问量 2万+

猜你喜欢

转载自blog.csdn.net/weixin_43107805/article/details/105374542
今日推荐