给定一副牌,每张牌上都写着一个整数。
此时,你需要选定一个数字 X,使我们可以将整副牌按下述规则分成 1 组或更多组:
- 每组都有 X 张牌。
- 组内所有的牌上都写着相同的整数。
仅当你可选的 X >= 2 时返回 true。
示例 1:
输入:[1,2,3,4,4,3,2,1]
输出:true
解释:可行的分组是 [1,1],[2,2],[3,3],[4,4]
示例 2:
输入:[1,1,1,2,2,2,3,3]
输出:false
解释:没有满足要求的分组。
个人思路:拿到一个数组,第一步肯定是要计算每个值的个数,假设1有a个,2有b个,3有c个,4有d个
,要满足题目条件,其本质就是找a,b,c,d
有没有公约数(1除外)。
例如示例1,对应的就是a=2,b=2,c=2,d=2
,有一个公约数2所以是True
C#
public bool HasGroupsSizeX(int[] deck)
{
//1:总长度小于2直接返回
if (deck.Length < 2)
return false;
//2:统计每个数出现的次数
//Key:该数字。 Value:该数字出现的次数
Dictionary<int, int> NumcntDic = new Dictionary<int, int>();
//遍历数组
for (int i = 0; i < deck.Length; i++)
{
//字典键中是否包含当前数,包含,即该数已存在,则加一,不包含,即不存在,则赋值为1
NumcntDic[deck[i]] = NumcntDic.ContainsKey(deck[i]) ? NumcntDic[deck[i]] + 1 : 1;
}
//3:找到字典值集合中的最小值
int minCnt = NumcntDic.Values.Min();
//如果最小值小于二,则返回false
if (minCnt < 2)
return false;
//若最小则是a,其他值是b
//若满足条件,则a和b中有一个大于1的公约数
//eg:如果最小值a=6,其他值b=15。因为a=6=2*3,b=15=3*5,存在一个大于1的公约数3
//所以可以每组分配3个,符合条件,返回true
//4:找到最小值a的约数大于1的集合
//公约数集合
List<int> comList = new List<int>();
//从1开始,到minCnt的算术平方根结束
for (int i = 1; i <= Math.Sqrt(minCnt); i++)
{
//取余等于0
if (minCnt % i == 0)
{
//只记录大于1的约数
if (i > 1)
comList.Add(i);
//若i与其对应的约数不同,则添加
if (i != minCnt / i)
comList.Add(minCnt / i);
}
}
//遍历所有其他值b
foreach (int curCnt in NumcntDic.Values)
{
//相同不用计算
if (curCnt != minCnt)
{
//遍历最小值约数集合
//由于遍历时,移除元素不方便
//所以规定:集合中已舍去的约数用-1代替
for (int i = 0; i < comList.Count; i++)
{
if (comList[i] != -1)
{
//当前约数是公约数,则继续查找
if (curCnt % comList[i] == 0)
continue;
//当前约数不是公约数,则舍去,赋值为-1
comList[i] = -1;
}
}
}
}
//最后检查约数集合里是否含有非-1值
return comList.Any(com => com != -1);
}