|
|
package class_2023_03_2_week;
|
|
|
|
|
|
// 给定正整数 n
|
|
|
// 返回在 [1, n] 范围内具有 至少 1 位 重复数字的正整数的个数。
|
|
|
// 测试链接 : https://leetcode.cn/problems/numbers-with-repeated-digits/
|
|
|
public class Code04_NumbersWithRepeatedDigits {
|
|
|
|
|
|
public static int numDupDigitsAtMostN(int n) {
|
|
|
if (n <= 10) {
|
|
|
return 0;
|
|
|
}
|
|
|
// n = 23645
|
|
|
// len = 5长度
|
|
|
// n = 723645
|
|
|
// offset = 100000
|
|
|
int len = 1;
|
|
|
int offset = 1;
|
|
|
int tmp = n / 10;
|
|
|
while (tmp > 0) {
|
|
|
len++;
|
|
|
offset *= 10;
|
|
|
tmp /= 10;
|
|
|
}
|
|
|
// n = 23645 5长度
|
|
|
// 1长度的有几个、2长度的有几个、3长度的有几个、4长度的有几个
|
|
|
// 直接公式决定
|
|
|
int noRepeat = 0;
|
|
|
for (int i = 1; i < len; i++) {
|
|
|
noRepeat += numAllLength(i);
|
|
|
}
|
|
|
// num = 723645
|
|
|
// 单独求6长度的,数字不重复的有几个
|
|
|
if (len <= 10) {
|
|
|
// int a = 0b 11
|
|
|
int status = 0b1111111111;
|
|
|
// n = 732645
|
|
|
// offset 100000
|
|
|
// n / offset = 7
|
|
|
// 1 2 3 ... 6 7-1
|
|
|
// ====
|
|
|
// n = 732645
|
|
|
// offset 100000
|
|
|
// 10000
|
|
|
noRepeat += ((n / offset) - 1) * numberRest(offset / 10, status ^ 1);
|
|
|
// n = 732645
|
|
|
// offset 100000
|
|
|
// 7.....
|
|
|
noRepeat += process(offset / 10, status ^ (1 << (n / offset)), n);
|
|
|
}
|
|
|
return n + 1 - noRepeat;
|
|
|
}
|
|
|
|
|
|
// 10进制长度必须为len的所有数中
|
|
|
// 每一位数字都不重复的数有多少
|
|
|
// 10
|
|
|
// 9 * 9
|
|
|
// 9 * 9 * 8
|
|
|
// 9 * 9 * 8 * 7
|
|
|
public static int numAllLength(int len) {
|
|
|
if (len > 10) {
|
|
|
return 0;
|
|
|
}
|
|
|
if (len == 1) {
|
|
|
return 10;
|
|
|
}
|
|
|
int ans = 9;
|
|
|
int cur = 9;
|
|
|
while (--len > 0) {
|
|
|
ans *= cur;
|
|
|
cur--;
|
|
|
}
|
|
|
return ans;
|
|
|
}
|
|
|
|
|
|
// n = 732645
|
|
|
// 10000
|
|
|
// status : 还剩哪些数字可以选
|
|
|
// 返回值 : 前面的数字都和n一样,剩下的位里有多少数字,剩下的数字都不同?
|
|
|
public static int process(int offset, int status, int n) {
|
|
|
if (offset == 0) {
|
|
|
// n = 732645
|
|
|
// 0
|
|
|
return 1;
|
|
|
}
|
|
|
int ans = 0;
|
|
|
// n = 732645
|
|
|
// off 100
|
|
|
// 732....
|
|
|
// 7320...
|
|
|
// 7321...
|
|
|
// 7322... X
|
|
|
// 7323... X
|
|
|
// 7324...
|
|
|
// 7325...
|
|
|
//
|
|
|
// n = 732645
|
|
|
// off 100
|
|
|
// 6
|
|
|
// 7326 % 10 = 6
|
|
|
int first = (n / offset) % 10;
|
|
|
for (int cur = 0; cur < first; cur++) {
|
|
|
if ((status & (1 << cur)) != 0) {
|
|
|
ans += numberRest(offset / 10, status ^ (1 << cur));
|
|
|
}
|
|
|
}
|
|
|
// n = 732645
|
|
|
// off 10
|
|
|
// 7326 ..
|
|
|
if ((status & (1 << first)) != 0) {
|
|
|
ans += process(offset / 10, status ^ (1 << first), n);
|
|
|
}
|
|
|
return ans;
|
|
|
}
|
|
|
|
|
|
// offset : 1000, 还剩4长度
|
|
|
// offset : 100000, 还剩7长度
|
|
|
// status : 还有哪些数字可以选
|
|
|
// 状态!
|
|
|
// 9 8 7 6 5 4 3 2 1 0
|
|
|
// 0 1 1 1 0 0 0 1 1 1
|
|
|
public static int numberRest(int offset, int status) {
|
|
|
// c 还有几种数字可以选!
|
|
|
// offset = 1000; 100 10 1 0
|
|
|
// c = 7 6 5 4 3
|
|
|
// ans = 1 * 7 * 6 * 5 * 4
|
|
|
int c = hammingWeight(status);
|
|
|
int ans = 1;
|
|
|
while (offset > 0) {
|
|
|
ans *= c;
|
|
|
c--;
|
|
|
offset /= 10;
|
|
|
}
|
|
|
return ans;
|
|
|
}
|
|
|
|
|
|
public static int hammingWeight(int n) {
|
|
|
n = (n & 0x55555555) + ((n >>> 1) & 0x55555555);
|
|
|
n = (n & 0x33333333) + ((n >>> 2) & 0x33333333);
|
|
|
n = (n & 0x0f0f0f0f) + ((n >>> 4) & 0x0f0f0f0f);
|
|
|
n = (n & 0x00ff00ff) + ((n >>> 8) & 0x00ff00ff);
|
|
|
n = (n & 0x0000ffff) + ((n >>> 16) & 0x0000ffff);
|
|
|
return n;
|
|
|
}
|
|
|
|
|
|
}
|