题意就是给出一个N,找到(0,N]范围内有多少个最少有一位重复的数字。比如121,1454符合要求,123不符合。
思路: 这种问题的第一步非常关键。关于重复的题大多都要反着做,让我们找有重复的,那我们把不重复的找出来再减去就是重复的。 这时我们考虑一个数字比如1942,我们把它分为两半,1000以下和1000以上。
①1000以下:这种情况非常简单,排列组合,找到个十百三个位数字不同有多少种排列就可以,数学问题,O(1)复杂度。
②1000以上:做一个map保存每一位的数字,从头开始保存1,百位十位个位就找02345678这八个数字的全排列,之后去十位个位找0235678这七位的全排列,最后就是除了1942四个数字的个数。
暂时是双100 用时0ms 空间忘记了。
func numDupDigitsAtMostN(N int) int { // 以1942为例 // 取出每一位,因为包括N,为了后面计算时方便,这里加1. var digits []int for i:=N+1;i>0;i/=10 { digits = append(digits, i%10) } // 情况① 取1000以下 re := 0 n := len(digits) for i:=1;i<n;i++ { re += 9 * proc(9, i-1) } // 情况② 1000以上 // 这里把数组翻过来 [3,4,9,1] -> [1,9,4,3] for i, j := 0, len(digits)-1; i < j; i, j = i+1, j-1 { digits[i], digits[j] = digits[j], digits[i] } m := make(map[int]int) // 逐位考虑 1->9->4->3 for i:=0; i<n; i++ { var j int // 这个判断去除首位为0的情况, 数字不能为 01942 if i == 0 { j = 1 } else { j = 0 } // 关键点。 在每位的数字如果曾经没出现过,就在这一位上做全排列。 for ;j<digits[i];j++ { if m[j] == 0 { re += proc(9-i, n-i-1) } } // 如果数字存在了就没必要再往下判断了,直接去下个i if m[digits[i]] > 0 { break } // 将第i位数字加入map m[digits[i]]++ } return N-re } // 排列组合 func proc(m, n int) int { if n == 0 { return 1 } return proc(m, n-1) * (m - n + 1) }
后来看了看这道题的分类是dp,大概有个想法但是没有捋顺,还需要多加锻炼。
记录每天解决的小问题,积累起来去解决大问题。