List<Integer> values = newArrayList<Integer>(); for (inti=0; i < 10000; ++i) { if (count[i] > 0) { values.add(count[i]); } }
for (intX=2; X <= N; ++X) { if (N % X == 0) { booleanflag=true; for (int v: values) { if (v % X != 0) { flag = false; break; } } if (flag) { returntrue; } } }
returnfalse; } }
[sol1-Python3]
1 2 3 4 5 6 7 8 9
classSolution: defhasGroupsSizeX(self, deck: List[int]) -> bool: count = collections.Counter(deck) N = len(deck) for X inrange(2, N + 1): if N % X == 0: ifall(v % X == 0for v in count.values()): returnTrue returnFalse
classSolution { int count[10000]; public: boolhasGroupsSizeX(vector<int>& deck){ int N = (int)deck.size(); for (int c: deck) count[c]++;
vector<int> values; for (int i = 0; i < 10000; ++i) { if (count[i] > 0) { values.emplace_back(count[i]); } }
for (int X = 2; X <= N; ++X) { if (N % X == 0) { bool flag = 1; for (int v: values) { if (v % X != 0) { flag = 0; break; } } if (flag) { returntrue; } } } returnfalse; } };
复杂度分析
时间复杂度:O(N^2),其中 N 是卡牌个数。最多枚举 N 个可能的 X,对于每个 X,要遍历的数字 i 最多有 N 个。
空间复杂度:O(N + C) 或 O(N),其中 C 是数组 deck 中数的范围,在本题中 C 的值为 10000。在 C++ 和 Java 代码中,我们先用频率数组 count 存储了每个数字 i 出现的次数 count}[i],随后将所有超过零的次数转移到数组 values 中,方便进行遍历,因此需要使用长度为 C 的 count 数组以及长度不超过 N 的 values 数组,空间复杂度为 O(N + C)。在 Python 代码中,我们直接使用哈希映射存储每个数字 i 以及出现的次数,因此空间复杂度为 O(N)。
方法二:最大公约数
思路和算法
由于方法一已经提及 X 一定为 count}_i 的约数,这个条件是对所有牌中存在的数字 i 成立的,所以我们可以推出,只有当 X 为所有 count}_i 的约数,即所有 count}_i 的最大公约数的约数时,才存在可能的分组。公式化来说,我们假设牌中存在的数字集合为 a, b, c, d, e,那么只有当 X 为
classSolution { int cnt[10000]; public: boolhasGroupsSizeX(vector<int>& deck){ for (auto x: deck) cnt[x]++; int g = -1; for (int i = 0; i < 10000; ++i) { if (cnt[i]) { if (~g) { g = gcd(g, cnt[i]); } else { g = cnt[i]; } } } return g >= 2; } };
复杂度分析
时间复杂度:O(N \log C),其中 N 是卡牌的个数,C 是数组 deck 中数的范围,在本题中 C 的值为 10000。求两个数最大公约数的复杂度是 O(\log C),需要求最多 N - 1 次。