⭐️⭐️⭐️

# 題目敘述

Two strings X and Y are similar if we can swap two letters (in different positions) of X , so that it equals Y . Also two strings X and Y are similar if they are equal.

For example, "tars" and "rats" are similar (swapping at positions 0 and 2), and "rats" and "arts" are similar, but "star" is not similar to "tars" , "rats" , or "arts" .

Together, these form two connected groups by similarity: {"tars", "rats", "arts"} and {"star"} . Notice that "tars" and "arts" are in the same group even though they are not similar. Formally, each group is such that a word is in the group if and only if it is similar to at least one other word in the group.

We are given a list strs of strings where every string in strs is an anagram of every other string in strs . How many groups are there?

# Example 1

Input: strs = [“tars”,“rats”,“arts”,“star”]
Output: 2

# Example 2

Input: strs = [“omv”,“ovm”]
Output: 1

# 解題思路

遍歷整個陣列將相似的字串組合為群組,最後輸出群組的數量。

比對字串相似的方法,如果差異的字元數量大於 2,則兩字串不相似。

Union Find 的部分可以參考這個文章 連結

# Solution

class Solution {
public:
    vector<int> parent, rank;
    int res;
    bool similar(string& s1, string& s2) {
        if (s1 == s2) return true;
        int diff = 0;
        for (int i=0; i<s1.size(); i++) {
            if (s1[i] != s2[i]) {
                diff++;
                if (diff > 2) return false;
            }
        }
        return diff == 2;
    }
    int find(int x) {
        if (x == parent[x]) return x;
        return parent[x] = find(parent[x]);
    }
    void unionSet(int x, int y) {
        int px = find(x), py = find(y);
        if (px == py) return;
        if (rank[px] > rank[py]) parent[py] = px;
        else if (rank[px] < rank[py]) parent[px] = py;
        else {
            parent[py] = px;
            rank[px] ++;
        }
        res--;
    }
    int numSimilarGroups(vector<string>& strs) {
        int n = strs.size();
        res = n;
        parent.resize(n);
        rank.resize(n);
        for (int i=0; i<n; i++) parent[i] = i;
        for (int i=0; i<n; i++) {
            for (int j=i+1; j<n; j++) {
                if (similar(strs[i], strs[j])) unionSet(i, j);
            }
        }
        return res;
    }
};
class Solution {
    class UnionFind {
        int[] parent, rank;
        int count;
        public UnionFind(int n) {
            parent = new int[n];
            rank = new int[n];
            count = n;
            for (int i = 0; i < n; i++) {
                parent[i] = i;
            }
        }
        public int find(int x) {
            if (parent[x] != x) {
                parent[x] = find(parent[x]);
            }
            return parent[x];
        }
        public boolean union(int x, int y) {
            int rootX = find(x);
            int rootY = find(y);
            if (rootX == rootY) {
                return false;
            }
            if (rank[rootX] < rank[rootY]) {
                int temp = rootX;
                rootX = rootY;
                rootY = temp;
            }
            parent[rootY] = rootX;
            if (rank[rootX] == rank[rootY]) {
                rank[rootX]++;
            }
            count--;
            return true;
        }
        public int getCount() {
            return count;
        }
    }
    public int numSimilarGroups(String[] strs) {
        int n = strs.length;
        UnionFind uf = new UnionFind(n);
        for (int i = 0; i < n; i++) {
            for (int j = i + 1; j < n; j++) {
                if (isSimilar(strs[i], strs[j])) {
                    uf.union(i, j);
                }
            }
        }
        return uf.getCount();
    }
    private boolean isSimilar(String s1, String s2) {
        if (s1.equals(s2)) {
            return true;
        }
        int diff = 0;
        for (int i = 0; i < s1.length(); i++) {
            if (s1.charAt(i) != s2.charAt(i)) {
                diff++;
                if (diff > 2) {
                    return false;
                }
            }
        }
        return diff == 2;
    }
}
class UnionFind:
    def __init__(self, n):
        self.parent = list(range(n))
        self.rank = [0] * n
        self.count = n
    def find(self, x):
        if self.parent[x] != x:
            self.parent[x] = self.find(self.parent[x])
        return self.parent[x]
    def union(self, x, y):
        root_x = self.find(x)
        root_y = self.find(y)
        if root_x == root_y:
            return False
        if self.rank[root_x] < self.rank[root_y]:
            root_x, root_y = root_y, root_x
        self.parent[root_y] = root_x
        if self.rank[root_x] == self.rank[root_y]:
            self.rank[root_x] += 1
        self.count -= 1
        return True
class Solution:
    def numSimilarGroups(self, strs: List[str]) -> int:
        def is_similar(s1, s2):
            if s1 == s2:
                return True
            diff = 0
            for c1, c2 in zip(s1, s2):
                if c1 != c2:
                    diff += 1
                    if diff > 2:
                        return False
            return diff == 2
        n = len(strs)
        uf = UnionFind(n)
        for i in range(n):
            for j in range(i + 1, n):
                if is_similar(strs[i], strs[j]):
                    uf.union(i, j)
        return uf.count