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] Remove 9 移除9的更多相关文章

  1. [LeetCode] Remove Element 移除元素

    Given an array and a value, remove all instances of that value in place and return the new length. T ...

  2. [LeetCode] Remove Comments 移除注释

    Given a C++ program, remove comments from it. The program source is an array where source[i] is the ...

  3. [LeetCode] Remove Boxes 移除盒子

    Given several boxes with different colors represented by different positive numbers. You may experie ...

  4. LeetCode:Remove Duplicates from Sorted List I II

    LeetCode:Remove Duplicates from Sorted List Given a sorted linked list, delete all duplicates such t ...

  5. LeetCode:Remove Duplicates from Sorted Array I II

    LeetCode:Remove Duplicates from Sorted Array Given a sorted array, remove the duplicates in place su ...

  6. 【转载】C#通过Remove方法移除DataTable中的某一列数据

    在C#中的Datatable数据变量的操作过程中,有时候我们需要移除当前DataTable变量中的某一列的数据,此时我们就需要使用到DataTable变量内部的Columns属性变量的Remove方法 ...

  7. 【转载】C#中List集合使用Remove方法移除指定的对象

    在C#的List集合操作中,有时候需要将特定的对象或者元素移除出List集合序列中,此时可使用到List集合的Remove方法,Remove方法的方法签名为bool Remove(T item),it ...

  8. [LeetCode] Remove Duplicate Letters 移除重复字母

    Given a string which contains only lowercase letters, remove duplicate letters so that every letter ...

  9. [LeetCode] Remove Invalid Parentheses 移除非法括号

    Remove the minimum number of invalid parentheses in order to make the input string valid. Return all ...

随机推荐

  1. Spring Framework 简介

    Spring Framework 依赖注入.事务管理.Web应用程序.数据访问.消息传递.测试和更多的核心支持. Tips: Spring 官网:https://spring.io/ spring f ...

  2. 数据库(Mongodb)

    1.MongoClient()函数 In [8]: import pymongo In [9]: con = pymongo.MongoClient('localhost') #建立连接 In [10 ...

  3. centos安装包选择--liveCD、liveDVD、bin-DVD、netinstall和minimal

    在Centos官方选择下载centos的时候有好几个文件可供下载,包括liveCD.liveDVD和bin-DVD等等.这些文件都有什么区别,我们应该选择哪个文件下载呢? liveDVD版本:它就是一 ...

  4. JavaScript(第二天)【语法,变量】

    一.语法构成 区分大小写 ECMAScript中的一切,包括变量.函数名和操作符都是区分大小写的.例如:text和Text表示两种不同的变量.   标识符 所谓标识符,就是指变量.函数.属性的名字,或 ...

  5. bug终结者 团队作业第八周

    bug终结者 团队作业第八周 本次任务 素材提供及编辑:20162328 蔡文琛 博客修改完善:20162322 朱娅霖 "bug终结者" 宏伟蓝图 UML 手绘底稿 用例图 选项 ...

  6. 第二周c语言PTA作业留

    6-1 计算两数的和与差(10 分) 本题要求实现一个计算输入的两数的和与差的简单函数. 函数接口定义: void sum_diff( float op1, float op2, float psum ...

  7. 开始 Python 之旅

    开始 Python 之旅 课程来源 本课程基于 Python for you and me 教程翻译制作,其中参考了 Python tutorial 和 The Python Standard Lib ...

  8. 为label或者textView添加placeHolder

    Tip:使用textView的代理需要在头文件中加入: <UITextViewDelegate> h文件 @interface FeedbackViewController : UIVie ...

  9. JAVA_SE基础——1.JDK&JRE下载及安装

    这是我学了JAVA来写的第一篇博客: 我首先是在传智播客领了张.毕向东老师的免费JAVA学习光盘来学习! 下面我来教大家安装使用JAVA时候必备的JDK 1.首先上甲骨文公司的官方网站下载JDK的安装 ...

  10. python的模块和包

    ==模块== python语言的组织结构层次: 包->模块->代码文件->类->函数->代码块 什么是模块呢 可以把模块理解为一个代码文件的封装,这是比类更高一级的封装层 ...