原题链接:https://leetcode.cn/problems/numbers-with-repeated-digits/
使用数位dp来做
func numDupDigitsAtMostN(n int) int {
s := strconv.Itoa(n)
m := len(s)
memo := make([][1 << 10]int, m)
for i := range memo {
for j := range memo[i] {
memo[i][j] = -1
}
}
for i := range memo {
for j := range memo[i] {
memo[i][j] = -1
}
}
// i表示当前选择的位置,mask表示当前已经选择过的数
// isLimit表示当前位置的数是否受限
// isNumber表示上一个位置是否跳过
var dfs func(i, mask int,isLimit, isNumber bool) int
dfs = func(i, mask int, isLimit, isNumber bool) int {
if i == m {
if isNumber {
return 1
}
return 0
}
// 必须判断isLimit和isNumber是因为
// 当!isNumber或者isLimit时, 是不会重复执行到的,所以无需记化
// 只有!isLimit和isNumber才需要记忆化
// 注意其实isLimit或isNumber的成立隐含着前面所有的字符都是isLimit和isNumber
if !isLimit && isNumber && memo[i][mask] != -1 {
return memo[i][mask]
}
res := 0
if !isNumber {
res += dfs(i+1, mask, false, false)
}
low := 1
if isNumber {
low = 0
}
high := 9
if isLimit {
high = int(s[i] - '0')
}
for j := low; j <= high; j ++ {
// 如果j在mask中,则不选择
if (mask >> j) & 1 != 1 {
res += dfs(i+1, mask | (1 << j), isLimit&&(j == high), true)
}
}
if isNumber && !isLimit {
memo[i][mask] = res
}
return res
}
return n - dfs(0, 0, true, false)
}