# 952. Largest Component Size by Common Factor

**<font color=red>难度: Hard</font>**

## 刷题内容

> 原题连接

* https://leetcode.com/problems/largest-component-size-by-common-factor/

> 内容描述

```
Given a non-empty array of unique positive integers A, consider the following graph:

There are A.length nodes, labelled A[0] to A[A.length - 1];
There is an edge between A[i] and A[j] if and only if A[i] and A[j] share a common factor greater than 1.
Return the size of the largest connected component in the graph.

 

Example 1:

Input: [4,6,15,35]
Output: 4

Example 2:

Input: [20,50,9,63]
Output: 2

Example 3:

Input: [2,3,6,7,4,12,21,39]
Output: 8

Note:

1 <= A.length <= 20000
1 <= A[i] <= 100000
```

## 解题方案

> 思路 1
******- 时间复杂度: O(NlgW)******- 空间复杂度: O(N)******

时间复杂度中的W是指A中的最大值

[My first post in LeetCode](https://leetcode.com/problems/largest-component-size-by-common-factor/discuss/200643/Python-1456-ms-beats-100-Union-Find-and-Prime-factor-decomposition-with-Optimization)


Using the input of first example,We can firstly do Prime factor decomposition for each number in A, 
then we can get the ```map:{2: [4, 6], 3: [6, 15], 5: [15, 35], 7: [35]})```, 
in which values are all multiples of the key(must be prime). 
Then we can regard the first multiple as the root of all the multiples, 
connect each multiple with the root. In this example, we ```connect 6 with 4(root)```, ```connect 15 with 6(root)```, 
```connect 35 with 15(root)```, because there is only one multiple in ```7: [35]```, so no connection. 
At the end, we can get the largest size 4, which is the result. The root of all the numbers in A is the number 4.


```python
class UnionFind(object):
    def uf(self, n):  
        self.uf = [i for i in range(n)]
        self.size = [1] * n
        
    def find(self, x):  
        while x != self.uf[x]:
            self.uf[x] = self.uf[self.uf[x]]
            x = self.uf[x]
        return self.uf[x]

    def union(self, x, y):  
        x_root = self.find(x)
        y_root = self.find(y)
        if x_root == y_root:
            return
        self.uf[x_root] = y_root
        self.size[y_root] += self.size[x_root]
        self.size[x_root] = 0


class Solution(object):
    def largestComponentSize(self, A):
        """
        :type A: List[int]
        :rtype: int
        """
        def primeFactors(n):  # Prime factor decomposition
            out = set()
            while n % 2 == 0: 
                out.add(2)
                n //= 2
            for i in range(3, int(math.sqrt(n))+1, 2): 
                while n % i== 0: 
                    out.add(i) 
                    n //= i 
            if n > 2: 
                out.add(n)
            return out
        
        idx_lookup = {A[i]: i for i in range(len(A))} # in order to find idx in uf
        uf = UnionFind()
        uf.uf(len(A))
        primeAndItsMultiples = collections.defaultdict(list) # {2: [4, 6], 3: [6, 15], 5: [15, 35], 7: [35]})
        
        for i in A:
            factors = primeFactors(i)
            for f in factors:
                primeAndItsMultiples[f].append(i)

        # we don't need to connect all the multiples of a prime, 
        # just use the first multiple as their root
        for idx, multiples in primeAndItsMultiples.items():
            if multiples:
                root = multiples[0] # use the first multiple as their root
                for node in multiples[1:]:
                    uf.union(idx_lookup[node], idx_lookup[root]) # connect node with root
                    
        return max(uf.size)
```


Inspired by [yanx1](https://leetcode.com/yanx1/) in the comment below,  `idx_lookup` and `primeAndItsMultiples` are kind of unnecessary... all we need is a dict primeToIndex that record the index of the element in A who has p as a prime factor.

This allows the runtime to be reduced to `1112 ms`



```python
class UnionFind(object):
    def uf(self, n):  
        self.uf = [i for i in range(n)]
        self.size = [1] * n
        
    def find(self, x):  
        while x != self.uf[x]:
            self.uf[x] = self.uf[self.uf[x]]
            x = self.uf[x]
        return self.uf[x]

    def union(self, x, y):  
        x_root = self.find(x)
        y_root = self.find(y)
        if x_root == y_root:
            return
        self.uf[x_root] = y_root
        self.size[y_root] += self.size[x_root]
        self.size[x_root] = 0


class Solution(object):
    def largestComponentSize(self, A):
        """
        :type A: List[int]
        :rtype: int
        """
        def primeFactors(n):  # Prime factor decomposition
            out = set()
            while n % 2 == 0: 
                out.add(2)
                n //= 2
            for i in range(3, int(math.sqrt(n))+1, 2): 
                while n % i== 0: 
                    out.add(i) 
                    n //= i 
            if n > 2: 
                out.add(n)
            return out
        
        uf = UnionFind()
        uf.uf(len(A))
        
        primeToIndex = {} 
        for i,a in enumerate(A):
            primes = primeFactors(a)
            for p in primes:
                if p in primeToIndex:
                    uf.union(i, primeToIndex[p])
                primeToIndex[p] = i
                
        return max(uf.size)
```
