leetcode 952. 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. 1 <= A.length <= 20000
  2. 1 <= A[i] <= 100000

解题思路:

看到题目一开始的想法就是遍历每一对元素,判断他们是否最大公因数大于1,如果大于1则加入一组。时间复杂度为O(N^2*o(gcd)) , 看一下数据规模肯定超时,所以否定这个思路,另想。

后来想是否能够按照素数成组,可是我又想到难道要算10000以内的素数吗?这也太可怕了。否定!

再后来去看votrubac大神的解答,嘿,人家就是按照素数成组(primes)的,只不过是算sqrt(100000)内的素数而已(《=313),而大于sqrt(100000)的素数则放到一个哈希表中,再查找就是了。为什么不把大素数放到primes里呢?因为如果元素中有因子是大素数(指的是大于sqrt(100000)的素数),一定不需要从最后一个primes内的素数因子遍历到313,因为313*401>100000,所以如果把大素数放到primes中,会有很多无用的遍历。所以放到哈希表中lprimes中;

class Solution {
public:
    int largestComponentSize(vector<int>& A) 
    {
        int res = 0 ;
        for(auto a : A)
        {
            int last_r = -1 ;
            for(int i = 0 ; i < primes.size() ; ++i)
            {
                if(a % primes[i] == 0)
                {
                    if(last_r == -1) num[find(i)] += 1 ; //在一开始的时候,将元素加到其属于的组中。
                    while(a % primes[i] == 0) a /= primes[i] ;
                    merge(last_r , i) ;
                    last_r = i ;
                }
            }
            
            if(a > 1) //a一定是大素数
            {
                if(lprimes.count(a) == 0) //a没有出现过
                {
                    if(last_r < 0) lprimes[a] = { -1 , 1} ;//且a就是元素本身,没有被上面的素数因子除过,所以大素数单独成组,且组中元素个数为1;
                    else lprimes[a] = {last_r , 0} ; //a有因子是大素数,且有primes中的素数因子,将大素数合并到last_r所属的组内。(因为已经在上面素数因子的遍历中算过了)
                }
                else // a出现过
                {
                    if(lprimes[a][0] == -1) //大素数单独成组
                    {
                        if(last_r < 0) lprimes[a][1]++ ; //a就是元素本身,所以大素数组元素个数要+1,比如出现多个401.
                        else
                        {
                            num[find(last_r)] += lprimes[a][1] ; //将大素数的组合并到last_r所属的组中
                            lprimes[a] = {last_r , 0} ;
                        }
                    }
                    else //大素数已经被合并到别的组了
                    {
                        if(last_r < 0) //a是元素本身
                        {
                            num[find(lprimes[a][0])] += 1 ; //所以在大素数所属组元素个数+1;
                        }
                        else merge(last_r , lprimes[a][0]) ; //a不是元素本身,将两组合并
                    }
                }
            }
        }
        
        for(int i = 0 ; i < num.size() ; ++i)
        {
            res = max(res , num[i]) ;
        }
        
        return res ;
    }
    
    void merge(int i , int j)
    {
        if(i < 0 || j < 0) return ; //避免不合法的合并;
        int x = find(i) ;
        int y = find(j) ;
        if(x < y)
        {
            root[y] = x ;
            num[x] += num[y] ;
            num[y] = 0 ;
        }
        else if(x > y)
        {
            root[x] = y ;
            num[y] += num[x] ;
            num[x] = 0 ;
        }
    }
    
    int find(int i)
    {
        return root[i] == -1 ? i : find(root[i]) ;  
    }

private :
    vector<int> primes= {2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59, 61, 67, 71, 73, 79, 83, 89, 97, 101,
    103, 107, 109, 113, 127, 131, 137, 139, 149, 151, 157, 163, 167, 173, 179, 181, 191, 193, 197,
    199, 211, 223, 227, 229, 233, 239, 241, 251, 257, 263, 269, 271, 277, 281, 283, 293, 307, 311, 313 } ;
    
    vector<int> root = vector<int>(65 , -1) ; //root的下标代表组号,root[i] == -1 ,说明该组最老的祖先是i;
    vector<int> num = vector<int>(65 , 0) ; //记录组中包含的元素个数
    unordered_map<int , vector<int>> lprimes ; //关键词是大素数 , 值是{大素数的祖先 , 以大素数为最老祖先的组中包含的元素个数}
};

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值