1061. Lexicographically Smallest Equivalent String  

Intuition

Union Find.

Approach

The trick here is to make the ‘smaller’ character the new ‘parent’ during the union() operation, instead of basing it on the size of connected components (rank).

Complexity

Time complexity

Assuming the number of unique characters is $S$, the union() operation will have a runtime of $O(\log S)$. If the length of s1 (or s2) is $N$, and the length of baseStr is $M$, it involves performing union() $N$ times and find() $M$ times. Therefore, the overall time complexity is $O((N+M)\log S)$. Given that $S$ is 26 in this case, it simplifies to $O(M+N)$.

Space complexity

$O(S)$, i.e, $O(1)$ to maintain the parent dict.

Code

class UnionFind:
    def __init__(self):
        self.parent = dict()
        for c in "abcdefghijklmnopqrstuvwxyz":
            self.parent[c] = c
    
    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):
        r1 = self.find(x)
        r2 = self.find(y)
        # trick
        if r1 > r2:
            r1, r2 = r2, r1
        self.parent[r2] = r1

class Solution:
    def smallestEquivalentString(self, s1: str, s2: str, baseStr: str) -> str:
        uf = UnionFind()
        for c1, c2 in zip(s1, s2):
            uf.union(c1, c2)
        
        res = []
        for c in baseStr:
            res.append(uf.find(c))
        return ''.join(res)