Golang —— Leetcode 1015. Numbers With Repeated Digits

题意就是给出一个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,大概有个想法但是没有捋顺,还需要多加锻炼。


记录每天解决的小问题,积累起来去解决大问题。 

猜你喜欢

转载自blog.csdn.net/Lazyboy_/article/details/88650621