1012. 至少有 1 位重复的数字
描述:给定正整数 N
,返回小于等于 N
且具有至少 1 位重复数字的正整数的个数。
输入:20
输出:1,解释:只有11
解题思路(逆向求解,先组合不重复的个数,再用总数减):
有重复数总和 = 输入数n - 没有重复的数总和
没有重复的数总和 = 1 到(n - 1)位没有重复数总和 + 第n位没有重复数字总和
输入:234
[1,9] = 9
[10,99] = 9 * 9
[100,234] 是重头戏,拆解如下:
百位:有0、1、2共3中选择,但首位为0前面已经计算过了,可排除0,还有1、2
选择1时,是十位和个位可从[0,9]中随机选,只要保证不重复即可,于是有:1 * 9 * 8
选择2时,拆解十位,十位有0、1、2、3共4中选择,但百位占用了2,排除2,还有0、1、3
a) 选择0、2,个位可从[0,9]还剩下的8位(排除百位和十位占用2位)中随机选择,于是有:1 * 2 * 8
b) 选择3,个位有0、1、2、3、4位,但前面占用了2位,排除2、3,于是有:1 * 1 * 3
综上所述:有重复数总和 = 234 - (9 + 9 * 9) - (1 * 9 * 8 + 1 * 2 * 8 + 3) = 53
代码实现如下:
class Solution { public int numDupDigitsAtMostN(int n) { if (n <= 10) { return 0; } List<Integer> nList = splitN(n); // 1 到(n-1)位不重复的数字,如:输入1124,计算[1-999]中不重复的数字,0-9组合即可9*9*8 + 9*9 + 9 int highPositionZeroNotSameCount = calculateHighPositionZeroNotSame(nList.size() - 1); // n位不重复的数字,如:输入1124,计算[1000-1124]中不重复的数字 int fullLengthNotSameCount = calculateFullLengthNotSame(nList); return n - highPositionZeroNotSameCount - fullLengthNotSameCount; } private List<Integer> splitN(int n) { List<Integer> nList = new ArrayList<>(); while (n > 0) { nList.add(n % 10); n = n / 10; } List<Integer> nListAsc = new ArrayList<>(); for (int i = (nList.size() - 1); i >= 0; i--) { nListAsc.add(nList.get(i)); } return nListAsc; } private int calculateHighPositionZeroNotSame(int size) { int notSameCount = 9; int canChooseNumber = 9; // 组合后的数量 int cCount = 9; for (int i = 0; i < size - 1; i++) { cCount = cCount * canChooseNumber--; notSameCount = notSameCount + cCount; } return notSameCount; } private int calculateFullLengthNotSame(List<Integer> nList) { int canChooseNumber = 9; int notSameCount = 0; Set<Integer> nSet = new HashSet<>(); for (int i = 0; i < nList.size(); i++) { int canChooseNumberCount = calculateCanChooseNumber(i, nList, nSet); nSet.add(nList.get(i)); // 若是为0,不需要再组合 if (canChooseNumberCount == 0) { canChooseNumber--; continue; } // 非当前数位的后面可以随机组合的情况 int chooseNumNotSameCount = permutation(nList.size() - i - 1, canChooseNumber--); notSameCount = notSameCount + canChooseNumberCount * chooseNumNotSameCount; } return notSameCount; } private int calculateCanChooseNumber(int targetIndex, List<Integer> nList, Set<Integer> nSet) { int targetN = nList.get(targetIndex); int canChooseNumber = targetN; // 若是首位,排除自己和0 if (targetIndex == 0) { return targetN - 1; } // 不包含当前位前面有重复的 if (nSet.size() != targetIndex) { return 0; } boolean lastNumber = targetIndex == (nList.size() - 1); if (lastNumber) { // 若是最后一位还包括自身 canChooseNumber = canChooseNumber + 1; } for (int i = 0; i < targetIndex; i++) { int currentNum = nList.get(i); // 若是最后一位,且和前面的相等,也需要减掉一个坑位,如11 boolean needSubtract = lastNumber && targetN == currentNum; // 减掉前面已存在坑位 if ((targetN > currentNum) || needSubtract) { canChooseNumber--; } } return canChooseNumber; } private int permutation(int size, int canChooseNumber) { if (size == 0) { return 1; } int notSameCount = canChooseNumber; for (int i = 0; i < size - 1; i++) { notSameCount = notSameCount * (--canChooseNumber); } return notSameCount; } }
https://leetcode-cn.com/problems/numbers-with-repeated-digits/solution/xian-zu-he-bu-zhong-fu-de-ge-shu-zai-yon-eqor/
这是10月份面试菜鸟时的一道算法题,当场手撕的话还是有点难度的==,好运哦亲们~
愿你悄悄的努力,遇见更好的自己~