LeetCode 738. 单调递增的数字 | Python

738. 单调递增的数字


题目来源:力扣(LeetCode)https://leetcode-cn.com/problems/monotone-increasing-digits/

题目


给定一个非负整数 N,找出小于或等于 N 的最大的整数,同时这个整数需要满足其各个位数上的数字是单调递增。

(当且仅当每个相邻位数上的数字 xy 满足 x <= y 时,我们称这个整数是单调递增的。)

示例 1:

输入: N = 10
输出: 9

示例 2:

输入: N = 1234
输出: 1234

示例 3:

输入: N = 332
输出: 299

说明: N 是在 [0, 10^9] 范围内的一个整数。

解题思路


思路:贪心算法

先审题,题目给定一个非负整数 N N N,要求找出小于或等于 N N N 的最大整数。同时要求这个所求的最大整数满足各位数上的数字单调递增的。

题目中这里定义的单调递增:

  • 当且仅当每个相邻位数上的数字(从左往右) x x x y y y 满足 x ≤ y x \leq y xy

在这里,题目要求最大整数,那么原数的高位应该尽可能的保留。

s i z e size size 表示数字的长度

原则上,从高位到低位遍历这个数时,当我们找到第一个 i i i 满足 s t r _ N [ i ] > s t r _ N [ i + 1 ] str\_N[i] > str\_N[i+1] str_N[i]>str_N[i+1] 时,令 s t r _ N [ i ] − 1 str\_N[i] - 1 str_N[i]1 然后将 [ i + 1 , s i z e − 1 ] [i+1, size-1] [i+1,size1] 这些后续的低位数字都变为 9 9 9,即能够得到小于 N N N 的最大整数。例如:

# 输入
N = 123433
# 输出
max = 123399

这里找到的第一个 i i i 为输入数字的 4 4 4,在这里大于下一位的 3 3 3,那么将这里的 4 4 4 减一,后续的低位都变为 9。结果也就是上面示例中输出的 123399 123399 123399

但是这里有个特殊的情况,当我们找到前面所述的符合条件 i i i 时,当将 s t r _ N [ i ] − 1 str\_N[i] - 1 str_N[i]1 后,使得 s t r _ N [ i ] str\_N[i] str_N[i] 不再满足 s t r _ N [ i − 1 ] < = s t r _ N [ i ] str\_N[i-1] <= str\_N[i] str_N[i1]<=str_N[i]。例如示例 3:

# 输入
332
# 输出
229

在这个例子中,所找到第一个满足的 i i i 时索引为 1 1 1 的数字 3 3 3,当令其减一后,得到 2 2 2 又小于前一位的 3 3 3,不再满足(非严格)单调递增。

那么遇到这种情况,我们应该往回处理,找到某个位置 j j j ,令其对应的数字减一之后,使得再次满足单调递增,然后将 j j j 后续的数字都变成 9 9 9

这里我们可以发现,会出现这种情况,是因为数字中有一部分连续相同的数字。再举一个例子:

# 索引:0123456
输入:  2233332

在这里我们从前往后遍历,找到索引为 5 5 5 满足条件,此时令 3 3 3 减一之后又不再满足(非严格)单调递增的条件。这个时候会往回处理,逐位减一,我们可以发现,在回到索引 2 2 2 时,令此处的 3 3 3 减一之后,此时将满足(非严格)单调递增的条件。

我们可以发现,往回处理遇到的最后一个 3 3 3,其实就是连续重复数字的第一个数字。那么我们可以考虑用变量来标记这个重复数字第一个数字的索引。

因为要求求得的最大整数(非严格)单调递增。这里我们还需要添加一个变量记录遍历时遇到的最大数,较大数更容易遇到比其小的数字,同时若是最大数都是连续重复的数字时,那么这里就能够辅助标记这个数字最开始的索引。

具体思路如下:

  • 从高位往低位遍历,定义 m a x _ v a l u e max\_value max_value 用于存储遇到的最大值, i d x idx idx 用于记录最大值所对应的索引;
  • 当遇到比 m a x _ v a l u e max\_value max_value 更大的数字时,更新 m a x _ v a l u e max\_value max_value i d x idx idx 的值;
  • 同时查找满足 s t r _ N [ i ] > s t r _ N [ i + 1 ] str\_N[i] > str\_N[i+1] str_N[i]>str_N[i+1] 的第一个 i i i,此时有两种可能:
    • 第一种情况,这里的 i i i i d x idx idx 指向的是同一个数字,属于不需要往回处理的情况;
    • 第二种情况,这里的 i i i i d x idx idx 对应的数,数值一样,但是 i d x idx idx 标记在连续重复数第一个出现的位置。
  • 当找到这样的 i i i 时,进行处理,令 i d x idx idx 对应的数减一,后续的数字都变为 9 9 9

具体的代码实现如下。

class Solution:
    def monotoneIncreasingDigits(self, N: int) -> int:
        if N < 10:
            return N

        str_N = list(str(N))
        size = len(str_N)

        max_value = '0'
        idx = size

        for i in range(size-1):
            # 寻找大值
            if max_value < str_N[i]:
                max_value = str_N[i]
                idx = i
            # 寻找满足条件的第一个 i
            if str_N[i] > str_N[i+1]:
                str_N[idx] = str(int(str_N[idx])-1)
                for j in range(idx+1, size):
                    str_N[j] = '9'
                break
        
        return int(''.join(str_N))

这道题也可以考虑从后往前遍历的方法来实现。有兴趣的话可以尝试一下。

欢迎关注


公众号 【书所集录


如有错误,烦请指出,欢迎指点交流。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值