Start from integer 1, remove any integer that contains 9 such as 9, 19, 29...

So now, you will have a new integer sequence: 1, 2, 3, 4, 5, 6, 7, 8, 10, 11, ...

Given a positive integer n, you need to return the n-th integer after removing. Note that 1 will be the first integer.

Example 1:

Input: 9
Output: 10

Hint: n will not exceed 9 x 10^8.

这道题让我们移除所有包含数字9的数字,然后得到一个新的数列,给了一个数字n,求在这个新的数组中第n个数字。多写些数字来看看:

0,1,2,3,4,5,6,7,8 (移除了9)

10,11,12,13,14,15,16,17,18 (移除了19)

.....

80,81,82,83,84,85,86,87,88 (移除了89)

(移除了 90 - 99 )

100,101,102,103,104,105,106,107,108 (移除了109)

可以发现,8的下一位就是10了,18的下一位是20,88的下一位是100,实际上这就是九进制的数字的规律,那么这道题就变成了将十进制数n转为九进制数,这个就没啥难度了,就每次对9取余,然后乘以 base,n每次自除以9,base 每次扩大10倍,参见代码如下:

解法一:

class Solution {
public:
int newInteger(int n) {
long res = , base = ;
while (n > ) {
res += n % * base;
n /= ;
base *= ;
}
return res;
}
};

我们也可以写的更简洁一些,不用 base 变量,将结果 res 先当作字符串来处理,最后再转回整型数,参见代码如下:

解法二:

class Solution {
public:
int newInteger(int n) {
string res = "";
while (n > ) {
res = to_string(n % ) + res;
n /= ;
}
return stoi(res);
}
};

将十进制数转为九进制只能算 Easy 的题目,既然这道题标记了 Hard,我们就不应该只满足于此。因为数字9是个特例,可以用上面的巧妙的解法,但如果要移除1到8中间的任意一个呢?上面的方法就不好使了,还是要来看看通用的解法。又来读 fun4LeetCode 大神的 paper 了,这次大神收着写的,不算太长,还是可以好好读一读的。这里不管是移出那个数字,新数组中的第n个数字的值m,都是要大于n本身的,将多出的数的个数用 f(1, m) 表示,则有:

m - f(, m) = n

要求m的话,就要先求出 f(1, m) 的值,然后加上n的值,就能得到m了。这道题无法直接求出m的值,而是采用一种迭代逼近的方法来算m。最开始的时候,让m为n,先求 f(1, n) 的值,比如说结果为k,然后再算 f(1, n + k) 的值,用得到的结果 k' 来更新k,再带入算 f(1, n + k),直到 k == f(1, n + k) 为止,那么此时的 n + k 就是要求的m。

下面来看如何计算 f(1, m),当然不可能遍历所有的数字,一位一位来查看有没有要移除的数字了,太不高效了。再来看看开头列举的前 99 个数字中移除9后剩下的数字,统计一下,总共去掉了 19 个包含9的数字。那么想一下,如果前 99 个数字中要移除所有包含2的数字,会去掉多少个?其实还是 19 个,可以发现,前 99 个数字,不论去掉哪个数字,都会去掉 19 个数字。这是一个很重要的发现,再来看看这19个数是怎么分布的,首先每 10 个数都一定会包含一个要移除的数,比如要移除的是9,每 10 个数都会有一个9出现,而在 90 几那一行,10 个数都会包含9,所以都要移除,那么可以总结出规律,非移除数开头的其他9行,各移除1个,移除数开头的 10 个都要移除,所以就有 10+9=19 个。好,那么这是前 99 个数的情况,那么前 999 个数又是什么情况呢?其实很类似,非移除数开头的9行各有 19 个,移除数开头的有 10x19 个,所以整个就是 19x19 个,所以 19 这个基数很重要。

好,下面来看看各位上的数字a跟要移除数d之间的关系。有三种关系,分别是小于,等于,大于:

1)当 a < d 时,比如说要移除的数字是6,那么a就是1到5中的数,我们知道,每 10 个数中只含有一个6,所以就要移除a个6就行了,如果a在百位上,就是是 a * 19 个,然后再加上下一位上移除的值,用等式来写就是:

T(, m) = a_i * (^i - ^i) + T(, m % ^i)

2)当 a = d 时,那么a此时为6,如果a是十位上的数,那么前面 [1, 59] 中的5个6要先移除掉,然后此时下一位有多少个数移除多个数,还要加上1。比如m如果是 63,那么 60, 61, 62, 63 这四个数要移除,怎么算的,通过 m%10 + 1 来计算,所以整个用等式来写就是:

T(, m) = a_i * (^i - ^i) + m % ^i + 

3)当 a > d 时,比如此时a为8,要移除的数字还是6,那么 [60, 69] 这 10 个数都要移除,那么实际上还要再移除7个6,分别是 [1,9], [10,19], [21,29], [31,39], [41,49], [51,59], [71,79] 这7个区间中的6,那么是怎么算的,通过 a - 1 来算,实际上是情况1的值再加上 10^i 个数,用等式来写就是:

T(, m) = (a_i - ) * (^i - ^i) + ^i + T(, m % ^i) = a_i * (^i - ^i) + ^i + T(, m % ^i)

参见代码如下:

解法三:

class Solution {
public:
int newInteger(int n) {
long d = , pre = , cur = ;
while (true) {
pre = cur;
cur = helper(n + cur, d);
if (cur == pre) break;
}
return n + cur;
}
long helper(long m, long d) {
long res = , p = , q = ;
for (long i = m; i >= ; i /= ) {
p *= ;
q *= ;
}
for (long i = m; i >= d; i %= p, p /= , q /= ) {
long a = i / p;
res += a * (p - q);
if (a == d) {
res += i % p + ; break;
} else if (a > d) {
res += q;
}
}
return res;
}
};

讨论:对于移除任意数字的一般情况,热心网友 majestyhao 给了一种更加简便的方法,这种解法是基于解法一而来的,是说不论移除任何数字,都先当作是移除9,统统都先转为九进制数,然后再对每一位上的数字做特殊处理,假如其大于等于要移除的数字,将就对应位上的数字加上个1,这里并不用担心进位的问题,因为九进制的数字不会出现9,现在是将原来的九进制数当作十进制数来做加法。博主试了一些 test cases,貌似没有什么问题,若各位看官大神们有不同意见的欢迎留言,代码参见评论区十一楼

Github 同步地址:

https://github.com/grandyang/leetcode/issues/660

参考资料:

https://leetcode.com/problems/remove-9/

https://leetcode.com/problems/remove-9/discuss/106561/One-Line-Java-Solution

https://leetcode.com/problems/remove-9/discuss/106573/Alternative-solution-applicable-to-the-general-case

https://leetcode.com/problems/remove-9/discuss/106578/Share-my-O(logn)-C%2B%2B-solution-with-thinking-process-and-explanation

LeetCode All in One 题目讲解汇总(持续更新中...)

[LeetCode] 660. Remove 9 移除9的更多相关文章

  1. [LeetCode] 27. Remove Element 移除元素

    Given an array nums and a value val, remove all instances of that value in-place and return the new ...

  2. [LeetCode]27. Remove Element移除元素

    Given an array nums and a value val, remove all instances of that value in-place and return the new ...

  3. leetcode 660. Remove 9

    Start from integer 1, remove any integer that contains 9 such as 9, 19, 29... So now, you will have ...

  4. [LeetCode] 82. Remove Duplicates from Sorted List II 移除有序链表中的重复项 II

    Given a sorted linked list, delete all nodes that have duplicate numbers, leaving only distinct numb ...

  5. LeetCode 80 Remove Duplicates from Sorted Array II [Array/auto] <c++>

    LeetCode 80 Remove Duplicates from Sorted Array II [Array/auto] <c++> 给出排序好的一维数组,如果一个元素重复出现的次数 ...

  6. LeetCode 26 Remove Duplicates from Sorted Array [Array/std::distance/std::unique] <c++>

    LeetCode 26 Remove Duplicates from Sorted Array [Array/std::distance/std::unique] <c++> 给出排序好的 ...

  7. [LeetCode] 80. Remove Duplicates from Sorted Array II ☆☆☆(从有序数组中删除重复项之二)

    https://leetcode.com/problems/remove-duplicates-from-sorted-array-ii/discuss/27976/3-6-easy-lines-C% ...

  8. [LeetCode] 82. Remove Duplicates from Sorted List II_Medium tag: Linked List

    Given a sorted linked list, delete all nodes that have duplicate numbers, leaving only distinctnumbe ...

  9. Leetcode练习题Remove Element

    Leetcode练习题Remove Element Question: Given an array nums and a value val, remove all instances of tha ...

随机推荐

  1. 【Wannafly挑战赛29F】最后之作(Trie树,动态规划,斜率优化)

    [Wannafly挑战赛29F]最后之作(Trie树,动态规划,斜率优化) 题面 牛客 题解 首先考虑怎么计算\([l,r]\)这个子串的不同的串的个数. 如果\(l=1\),我们构建\(Trie\) ...

  2. 1. mvc 树形控件tree + 表格jqgrid 显示界面

    1.界面显示效果 2.资源下载 地址 1. jstree  https://www.jstree.com/   2.表格jqgrid  https://blog.mn886.net/jqGrid/  ...

  3. Java面向对象——类的成员

    Java面向对象——类的成员 摘要:本文主要介绍了类的常见成员. 属性 属性称为成员变量,一般来讲不用赋值,因为有默认值,另外显式赋值没有意义会导致所有由此类创建对象都是此值. 默认值 Boolean ...

  4. jQuery HTML/CSS 方法大全

    下表列出了用于操作HTML和CSS的所有方法. 方法 描述 addClass() 向所选元素添加一个或多个类名 after() 在所选元素之后插入内容 append() 在所选元素的末尾插入内容 ap ...

  5. 云顶之弈换中立python脚本

    import pynput keyboard = pynput.keyboard.Controller() mouse = pynput.mouse.Controller() def on_relea ...

  6. 切换tab栏echarts错位的问题

    在使用echarts的时候页面中有tab栏的时候经常遇到echarts错位的情况 解决方法一.在点击tab栏的时候进行页面中的echarts初始化 在多层tab栏存在的时候eachrts的容器布局是百 ...

  7. CTF必备技能丨Linux Pwn入门教程——环境配置

    说在前面 这是一套Linux Pwn入门教程系列,作者依据Atum师傅在i春秋上的Pwn入门课程中的技术分类,并结合近几年赛事中出现的一些题目和文章整理出一份相对完整的Linux Pwn教程. 问:为 ...

  8. Java 打印HelloKitty

    Java第一课 如何在控制台打印出"Hello Kitty" 如图所示,在IDE中使用 System.out.println(); 语句来实现打印 最后附上AIDE下载链接: Ja ...

  9. Properties类按顺序输出加载内容

    Properties类按顺序输出加载内容 最近手写工厂的时候,遇到了加载配置文件时不按照properties文件中的数据的顺序来加载. 一.问题代码 import java.io.IOExceptio ...

  10. pipenv 管理虚拟环境

    pipenv --python 3.6 创建虚拟环境 vim Pipfile —> 修改源 为阿里云镜像 https://mirrors.aliyun.com/pypi/simple [pack ...