《剑指 Offer》(第 2 版) 题解(Python 语言实现)第 41-50 题


第 41 题:数据流中的中位数

传送门:AcWing:数据流中的中位数

如何得到一个数据流中的中位数?

如果从数据流中读出奇数个数值,那么中位数就是所有数值排序之后位于中间的数值。

如果从数据流中读出偶数个数值,那么中位数就是所有数值排序之后中间两个数的平均值。

样例

输入:1, 2, 3, 4

输出:1,1.5,2,2.5

解释:每当数据流读入一个数据,就进行一次判断并输出当前的中位数。

参考资料:LeetCode 第 295 题:数据流的中位数

image-20190107113527615

image-20190107113606573

问题:1、上面的思路是很简单的,想想用 Python 如何实现。

2、LeetCode 上面第 4 题:排序数组的中位数如何求?

另一种使用堆的解法:这道题是堆解决的问题。

用两个堆:max heap 和 min heap,再一个median值, 维持两个堆的大小相等(max堆可以比min堆多一个). 对于新来的元素,比较新元素和median的大小,如果大于median就放入最小堆里面,如果小于median就放入最大堆里面,如果max heap,和min heap不平衡了,就调整一下。 然后调整过后median 里面的值就是我们要求的中位数。

image-20190119134053646

C++ 代码:

image-20190122114637528

第 42 题:连续子数组的最大和

传送门:连续子数组的最大和

输入一个 非空 整型数组,数组里的数可能为正,也可能为负。

数组中一个或连续的多个整数组成一个子数组。

求所有子数组的和的最大值。

要求时间复杂度为 O ( n ) O(n) O(n)

样例:

输入:[1, -2, 3, 10, -4, 7, 2, -5]

输出:18

同 LeetCode 第 53 题,题解传送门:LeetCode 第 53 题:连续子数组的最大和

“大雪菜”的做法:状态:以前一个数结尾的“连续子数组的最大和”为状态。

C++ 代码:

image-20190122120302354

###第 43 题:整数中 1 出现的次数(从 1 到 n 整数中 1 出现的次数)

传送门:AcWing:从 1 到 n 整数中 1 出现的次数

输入一个整数 n n n,求从 1 1 1 n n n n n n 个整数的十进制表示中 1 1 1 出现的次数。

例如输入 12 12 12,从 1 1 1 12 12 12 这些整数中包含 1 1 1 的数字有 1 1 1 10 10 10 11 11 11 12 12 12,“ 1 1 1” 一共出现了 5 5 5 次。

样例:

输入: 12
输出: 5

同 LeetCode 第 233 题:数字 1 1 1 的个数。

大雪菜的解法:

C++ 代码:

image-20190122121945761

思路:

image-20190122145235865

Python 代码:

# 56. 从1到n整数中1出现的次数
#
# 输入一个整数n,求从1到n这n个整数的十进制表示中1出现的次数。
#
# 例如输入12,从1到12这些整数中包含“1”的数字有1,10,11和12,其中“1”一共出现了5次。
#
# 样例
# 输入: 12
# 输出: 5
class Solution(object):
    def numberOf1Between1AndN_Solution(self, n):
        """
        :type n: int
        :rtype: int
        """
        if n <= 0:
            return 0

        number = []
        while n:
            number.append(n % 10)
            n //= 10

        res = 0
        for i in range(len(number) - 1, -1, -1):
            left = 0
            right = 0
            # 想清楚这里 t 为什么从 1 开始
            t = 1
            for j in range(len(number) - 1, i, -1):
                left = left * 10 + number[j]

            for j in range(i - 1, -1, -1):
                right = right * 10 + number[j]
                t *= 10
            # print(left, right)
            # 至少有左边的数这么多
            res += left * t
            # print(number[i], left, right, t, left * t)
            if number[i] == 1:
                res += right + 1
            elif number[i] > 1:
                res += t
        return res


if __name__ == '__main__':
    solution = Solution()
    n = 45032
    result = solution.numberOf1Between1AndN_Solution(n)
    print('result', result)

解法1:从 1 1 1 n n n 遍历,每个数通过对 10 10 10 求余数判断整数的个位数字是不是 1 1 1,大于 10 10 10 的除以 10 10 10 之后再判断。我们对每个数字都要做除法和求余运算以求出该数字中 1 1 1 出现的次数。如果输入数字 n n n n n n O ( log ⁡ n ) O(\log n) O(logn) 位,我们需要判断每一位是不是 1 1 1,那么时间复杂度为 O ( n ∗ log ⁡ n ) O(n* \log n) O(nlogn)。这样做,计算量大,效率不高。

本文采用《数学之美》上面提出的方法,设定整数点(如 1 1 1 10 10 10 100 100 100等等)作为位置点 i i i(对应 n n n的个位、十位、百位等等),分别对每个数位上有多少包含 1 1 1 的点进行分析。

根据设定的整数位置,对 n n n 进行分割,分为两部分,高位 n / i n/i n/i,低位 n % i n \% i n%i

1、当 i i i 表示百位,且百位对应的数 ≥ 2 \ge2 2

例如 n = 31456 n=31456 n=31456,此时考虑 i = 100 i=100 i=100,则 a = 314 a=314 a=314 b = 56 b=56 b=56

此时百位为 1 1 1 的次数有 a / 10 + 1 = 32 a/10+1=32 a/10+1=32 批次,具体如下:

说明:第 1 批次: 00100 − 00199 00100-00199 0010000199,一共 100 100 100 个数;

第 2 批次: 01100 − 01199 01100-01199 0110001199,一共 100 100 100 个数;

……

第 32 批次: 31100 − 31199 31100-31199 3110031199,一共 100 100 100 个数;

最高两位 0 − 31 0-31 031,每一批次都包含 100 100 100 个连续的点,即共有 ( a / 10 + 1 ) × 100 (a/10+1)\times100 (a/10+1)×100 个点的百位为 1 1 1

2、当 i i i 表示百位,且百位对应的数为 1 1 1

例如 n = 31156 n=31156 n=31156 i = 100 i=100 i=100,则 a = 311 a=311 a=311 b = 56 b=56 b=56,此时百位对应的就是 1 1 1

第 1 批次: 00100 − 00199 00100-00199 0010000199,一共 100 100 100 个数;

第 2 批次: 01100 − 01199 01100-01199 0110001199,一共 100 100 100 个数;

……

第 31 批次: 30100 − 30199 30100-30199 3010030199,一共 100 100 100 个数;

第 32 批次: 31100 − 311569 31100-311569 31100311569,一共 57 57 57 个数;

则共有 a / 10 a/10 a/10 次是包含 100 100 100 个连续点,最高两位 0 − 30 0-30 030

当最高两位为 31 31 31(即 a = 311 a=311 a=311),本次只对应局部点 00 − 56 00-56 0056,共 b + 1 b+1 b+1次,所有点加起来共有 ( a / 10 × 100 ) + ( b + 1 ) (a/10\times100)+(b+1) a/10×100+(b+1),这些点百位对应为 1 1 1;

3、当 i i i 表示百位,且百位对应的数为 0 0 0,如 n = 31056 n=31056 n=31056 i = 100 i=100 i=100,则 a = 310 a=310 a=310 b = 56 b=56 b=56

第 1 批次: 00100 − 00199 00100-00199 0010000199,一共 100 100 100 个数;

第 2 批次: 01100 − 01199 01100-01199 0110001199,一共 100 100 100 个数;

……

第 31 批次: 30100 − 30199 30100-30199 3010030199,一共 100 100 100 个数;

第 32 批次: 31000 − 31056 31000-31056 3100031056,一共 0 0 0 个数;

此时百位为 1 1 1 的次数有 a / 10 = 31 a/10=31 a/10=31,最高两位 0 − 30 0-30 030

综合以上 3 3 3 种情况,当百位对应 0 0 0 ≥ 2 \ge2 2 时,有 ( a + 8 ) / 10 (a+8)/10 (a+8)/10 次包含所有 100 100 100 个点,还有当百位为 1 1 1 a % 10 = = 1 a\%10==1 a%10==1),需要增加局部点 b + 1 b+1 b+1

之所以补 8 8 8,是因为当百位为 0 0 0,则 a / 10 = = ( a + 8 ) / 10 a/10==(a+8)/10 a/10==(a+8)/10,当百位 ≥ 2 \ge2 2,补 8 8 8 会产生进位,效果等同于 ( a / 10 + 1 ) (a/10+1) (a/10+1)

Python 代码:

class Solution:
    def NumberOf1Between1AndN_Solution(self, n):
        # write code here
        count = 0
        i = 1
        while i <= n:
            a = n / i
            b = n % i
            count += (a+8) / 10 * i + (a % 10 == 1)*(b + 1)
            i *= 10
        return count

参考资料:https://blog.csdn.net/qq_38211852/article/details/80863364

https://cuijiahua.com/blog/2017/12/basis_31.html

第 44 题:数字序列中某一位的数字

传送门:数字序列中某一位的数字

数字以 0123456789101112131415… 的格式序列化到一个字符序列中。

在这个序列中,第 5 位(从 0 开始计数)是 5 ,第 13 位是 1 ,第 19 位是 4 ,等等。

请写一个函数求任意位对应的数字。

样例:

输入:13

输出:1

Python 代码:参考了 LeetCode 第 400 题讨论区代码

class Solution(object):
    def digitAtIndex(self, n):
        """
        :type n: int
        :rtype: int
        """

        # 如果 n 小于 10 ,直接返回就可以了
        if n < 10:
            return n

        # 计算前缀部分

        base = 9
        digits = 1
        # 2 位数,从 10 到 99 一共 ( 99 - 10 + 1) * 2 = 90 * 2 = 180 位
        # 3 位数,从 100 到 999 一共 ( 999 - 100 + 1) * 2 = 900 * 3 = 2700 位
        # 4 位数,从 1000 到 9999 一共 ( 9999 - 1000 + 1) * 2 = 9000 * 4 = 3600 位

        while n - base * digits > 0:
            n -= base * digits
            base *= 10
            digits += 1


        index = n % digits
        if index == 0:
            # 计算出 num 是多少
            # 例如:192,有 1 个位移的偏差
            num = 10 ** (digits - 1) + n // digits - 1
            # 返回个位就可以了
            return num % 10
        else:
            # 不能整除,那个偏移就不用算了
            # 例如 194 = 189 + 5
            # 100 + 2 = 102
            num = 10 ** (digits - 1) + n // digits
            # 从左边向右边数,第 2 位
            for i in range(index, digits):
                num //= 10
            return num % 10

参考资料:

1、https://www.acwing.com/activity/content/code/content/20758/

2、https://blog.csdn.net/Koala_Tree/article/details/79536284

image-20190123174301075

思路:跳过不同位数的数字,在相应位数中寻找。

以序列中第 1001 1001 1001 位为例:

1、序列前 10 10 10 位为 0 0 0 9 9 9,跳过,再从后面找 991 991 991 位;

2、后面 180 180 180 位为 10 10 10 99 99 99,因为一共 99 − 10 + 1 = 90 99-10+1=90 9910+1=90 个数,每个数 2 2 2 位,所以 180 180 180 位; 跳过,再从后面找 811 811 811 位( 1001 − 10 − 180 = 811 1001-10-180=811 100110180=811);

后面 2700 2700 2700 位为 100 100 100 999 999 999,因为 811 &lt; 2700 811&lt;2700 811<2700,所以 811 811 811 位是某个三位数中的一位;
由于811=270*3+1,这就是说811位是从100开始的第270个数字即370的中间一位,即7。(注意,这里都是从第0位开始计数的)


作者:Koala_Tree
来源:CSDN
原文:https://blog.csdn.net/Koala_Tree/article/details/79536284
版权声明:本文为博主原创文章,转载请附上博文链接!

Java 代码:

class Solution {  // 9*1  9*10*2  9*10*10*3
    public int digitAtIndex(int n) {
        int len = 1;
        long count = 9;
        int start = 1;

        while (n > len * count) {  //13  n=n-9=4 len=2 count=90 start=10 
            n -= len * count;      //start=10+3/2=11   答案是11的第二个1
            len += 1;
            count *= 10;
            start *= 10;
        }
        // start 记录当前循环区间的第一个数字,当 n 落到某一个确定的区间里了,
        // 那么 (n-1)/len 就是目标数字在该区间里的坐标,加上 start 就是得到了目标数字
        start += (n - 1) / len;
        String s = Integer.toString(start);
        return Character.getNumericValue(s.charAt((n - 1) % len));

    }
}

参考资料:这篇简书上的文章有详细步骤。https://www.jianshu.com/p/0bbf1fcbe070。

同 LeetCode 第 400 题:400. 第 N 个数字

说明:只不过 LeetCode 第 400 题从 1 开始,传送门:400. 第 N 个数字

在无限的整数序列 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, …中找到第 n 个数字。

注意:
n 是正数且在32为整形范围内 ( n < 231)。

示例 1:

输入:
3

输出:
3

示例 2:

输入:
11

输出:
0

说明:
第11个数字在序列 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, ... 里是0,它是10的一部分。

Python 代码:

class Solution:
    def findNthDigit(self, n):
        """
        :type n: int
        :rtype: int
        """
        # 特判:如果 n 小于 10 ,直接返回就可以了
        if n < 10:
            return n

        # 表示几位数
        # 2 位数,从 10 到 99 一共 ( 99 - 10 + 1) * 2 = 90 * 2 = 180 位
        # 3 位数,从 100 到 999 一共 ( 999 - 100 + 1) * 2 = 900 * 3 = 2700 位
        # 4 位数,从 1000 到 9999 一共 ( 9999 - 1000 + 1) * 2 = 9000 * 4 = 3600 位

        # 步骤1:calculate how many digits the number has

        # 计算前缀部分
        length = 0
        base = 9
        digits = 1

        # n = 1001 时,9 过,180 过,剩下 812

        # 不越界才加,要清楚这一点
        while length + base * digits < n:
            length += base * digits
            base *= 10
            digits += 1

        n -= length

        # step 2. calculate what the number is
        # 到这里,num 是 "digits 位数" 中的某一个数字
        # 以 digits = 3 为例,n 是 100 - 999 中的一位,num 表示是哪个数字

        index = n % digits

        if index == 0:
            # 如果整除,就是那个数字的最后一位
            num = 10 ** (digits - 1) + n // digits - 1
            return num % 10
        else:
            num = 10 ** (digits - 1) + n // digits
            for i in range(index, digits):
                num //= 10
            return num % 10


if __name__ == '__main__':
    solution = Solution()
    n = 190
    result1 = solution.findNthDigit(n)
    print(result1)

第 45 题:把数组排成最小的数

传送门:把数组排成最小的数

输入一个正整数数组,把数组里所有数字拼接起来排成一个数,打印能拼接出的所有数字中最小的一个。

例如输入数组 [3, 32, 321],则打印出这 3 个数字能排成的最小数字 321323

样例:

输入:[3, 32, 321]

输出:321323

注意:输出数字的格式为字符串。

同 LeetCode 第 179 题,最大数

Java 代码:

public String PrintMinNumber(int[] numbers) {
    int len = numbers.length;
    if (len == 0) {
        return "";
    }
    String[] numsStr = new String[len];
    for (int i = 0; i < len; i++) {
        numsStr[i] = numbers[i] + "";
    }
    Arrays.sort(numsStr, (a, b) -> (a + b).compareTo(b + a));
    StringBuilder builder = new StringBuilder();
    for (int i = 0; i < len; i++) {
        builder.append(numsStr[i]);
    }
    return builder.toString();
}

说明:其实就定义自定义排序规则。Python3 想用 cmp 而不是 key 的话,需要 from functools import cmp_to_key ,然后 sort 或者 sorted 的时候 key = cmp_to_key(your_comparator)

Python 代码:

class Solution(object):
    def printMinNumber(self, nums):
        """
        :type nums: List[int]
        :rtype: str
        """

        if len(nums) == 0:
            return ''
        # 自定义排序规则
        from functools import cmp_to_key
        key_func = cmp_to_key(lambda a, b: int(a + b) - int(b + a))
        result = sorted(map(str, nums), key=key_func)
        return ''.join(result)


if __name__ == '__main__':
    list1 = [7, -8, 5, 4, 0, -2, -5]
    # 要求:1、正数在前负数在后
    # 2、正数从小到大
    # 3、负数从大到小

    result = sorted(list1, key=lambda x: (x < 0, abs(x)))
    print(result)

    s = 'asdf234GDSdsf23'  # 排序:小写-大写-奇数-偶数

    print(
        "".join(
            sorted(
                s,
                key=lambda x: (
                    x.isdigit(),
                    x.isdigit() and int(x) %
                    2 == 0,
                    x.isupper(),
                    x))))

另一种写法:

Python 代码:

class NumCompare(str):
    # 注意:这里继承 str 类
    def __lt__(self, other):
        return self + other < other + self


class Solution(object):
    def printMinNumber(self, nums):
        """
        :type nums: List[int]
        :rtype: str
        """

        if len(nums) == 0:
            return ''
        # 自定义排序规则
        result = sorted(map(str, nums), key=NumCompare)
        return ''.join(result)

第 46 题:把数字翻译成字符串

传送门:把数字翻译成字符串

给定一个数字,我们按照如下规则把它翻译为字符串:

0 翻译成 “a”,1 翻译成 “b”,……,11 翻译成“l”,……,25 翻译成 “z”。

一个数字可能有多个翻译。例如 12258 有 5 种不同的翻译,它们分别是 “bccfi”、“bwfi”、“bczi”、“mcfi” 和 “mzi”。

请编程实现一个函数用来计算一个数字有多少种不同的翻译方法。

样例:

输入:“12258”

输出:5

思路:同 LeetCode 第 91 题 Decode Ways。使用动态规划。状态:dp[i] 表示 s[0,i] (包括 i ),一共有多少种翻译的方法。分类讨论:1、当前字符可以单独翻译;2、当前字符可以和前面一个字符一起翻译。dp[i] 就是以上二者之和。

Python 代码:

class Solution:
    def getTranslationCount(self, s):
        """
        :type s: str
        :rtype: int
        """
        s = str(s)
        l = len(s)
        if l == 0:
            return 0
        dp = [None for _ in range(l)]

        # dp[i] 表示 s[0,i] ,包括 i ,一共有多少种翻译的方法

        dp[0] = 1
        for i in range(1, l):
            # 当前值至少是 dp[i-1],因为 s[i] 一定可以单独翻译
            cur = dp[i - 1]

            # 看一看 s[i-1,i] 是不是可以翻译
            if 9 < int(s[i - 1:i + 1]) < 26:
                if i - 2 < 0:
                    # 12
                    cur += 1
                else:
                    # 要考虑到数组下标越界问题
                    cur += dp[i - 2]
            dp[i] = cur
        return dp[l - 1]

LeetCode 第 91 题:解码方法

传送门:91. 解码方法

一条包含字母 A-Z 的消息通过以下方式进行了编码:

'A' -> 1
'B' -> 2
...
'Z' -> 26

给定一个只包含数字的非空字符串,请计算解码方法的总数。

示例 1:

输入: "12"
输出: 2
解释: 它可以解码为 "AB"(1 2)或者 "L"(12)。

示例 2:

输入: "226"
输出: 3
解释: 它可以解码为 "BZ" (2 26), "VF" (22 6), 或者 "BBF" (2 2 6) 。

Python 代码:

# 91、解码方法
# 一条包含字母 A-Z 的消息通过以下方式进行了编码:
#
# 'A' -> 1
# 'B' -> 2
# ...
# 'Z' -> 26
# 给定一个只包含数字的非空字符串,请计算解码方法的总数。


class Solution:

    def numDecodings(self, s):
        """
        :type s: str
        :rtype: int
        """

        l = len(s)
        if l == 0:
            return 0

        if l == 1:
            return 1 if s[0] != '0' else 0
        dp = [0 for _ in range(l)]
        dp[0] = 1 if s[0] != '0' else 0
        for i in range(1, l):
            if s[i] != '0':
                # 如果不是 '0' ,那么 s[i] 就可以编码,所以 cur 就至少是  dp[i-1]
                dp[i] += dp[i - 1]
            if 9 < int(s[i - 1:i + 1]) < 27:
                # 可以和前面的数字组成一个编码
                if i - 2 < 0:
                    # 12
                    dp[i] += 1
                else:
                    dp[i] += dp[i - 2]
        return dp[l - 1]


if __name__ == '__main__':
    test_str = '12'
    s = Solution()
    res = s.numDecodings(test_str)
    print(res)

第 47 题:礼物的最大价值

传送门:礼物的最大价值

在一个 m×n 的棋盘的每一格都放有一个礼物,每个礼物都有一定的价值(价值大于0)。

你可以从棋盘的左上角开始拿格子里的礼物,并每次向右或者向下移动一格直到到达棋盘的右下角。

给定一个棋盘及其上面的礼物,请计算你最多能拿到多少价值的礼物?

注意:

  • m,n>0

样例:

输入:

[
     [2,3,1],
     [1,7,1],
     [4,6,1]
]

输出:19

解释:沿着路径 2→3→7→6→1 可以得到拿到最大价值礼物。

思路:动态规划。礼物要么来自左边一格,要么来自上面一格,两者取最大。要特殊判断的就是边界情况。另外可以使用一维数组完成动态规划。如果可以修改 grid,直接在 grid 上修改就可以了,不用辅助空间。

Python 代码:

class Solution(object):
    def getMaxValue(self, grid):
        """
        :type grid: List[List[int]]
        :rtype: int
        """

        m = len(grid)
        if m == 0:
            return 0
        n = len(grid[0])

        dp = [None for _ in range(n)]

        dp[0] = grid[0][0]
        for i in range(1, n):
            dp[i] = dp[i - 1] + grid[0][i]

        for i in range(1, m):
            for j in range(n):
                if j == 0:
                    dp[j] += grid[i][0]
                else:
                    dp[j] = grid[i][j] + max(dp[j - 1], dp[j])

        return dp[n - 1]

第 48 题:最长不重复字符串的子字符串

传送门:最长不含重复字符的子字符串

请从字符串中找出一个最长的不包含重复字符的子字符串,计算该最长子字符串的长度。

假设字符串中只包含从’a’到’z’的字符。

样例:

输入:“abcabc”

输出:3

思路1:滑动窗口:

Python 代码:

class Solution:
    def lengthOfLongestSubstring(self, s):
        """
        :type s: str
        :rtype: int
        """
        # 特判
        size = len(s)
        if size < 2:
            return size
        l = 0
        r = -1
        counter = [0 for _ in range(256)]
        res = 1
        while l < size:
            # 首先"右指针"不断向右边尝试,走到出现重复的最右边
            while r + 1 < size and counter[ord(s[r + 1])] == 0:
                # 表示没有重复元素,r 可以加 1
                counter[ord(s[r + 1])] += 1
                r += 1
            # 此时,记录不重复子串是"左指针"固定时候最长
            res = max(res, r - l + 1)
            # 考虑移动"左指针"
            # 此时 s[r+1] 就是已经扫过的区间里重复的元素,要把它剔除出去
            while r + 1 < size and s[l] != s[r + 1]:
                # 有重复元素,左边就要减 1
                counter[ord(s[l])] -= 1
                l += 1
            # 出了上一个循环以后,还要再把左指针向右移动一格,否则右指针不能向右移动
            counter[ord(s[l])] -= 1
            l += 1
        return res

思路2:动态规划

Python 代码:

class Solution:
    def lengthOfLongestSubstring(self, s):
        """
        :type s: str
        :rtype: int
        """
        # 特判
        l = len(s)
        if l < 2:
            return l
        # dp[i] 表示以 s[i] 结尾的最长不重复子串的长度
        # 因为自己肯定是不重复子串,所以初始值设置为 1
        dp = [1 for _ in range(l)]
        map = dict()
        map[s[0]] = 0
        for i in range(1, l):
            if s[i] in map:
                if i - map[s[i]] > dp[i - 1]:
                    dp[i] = dp[i - 1] + 1
                else:
                    dp[i] = i - map[s[i]]
            else:
                dp[i] = dp[i - 1] + 1
            # 设置字符与索引键值对
            map[s[i]] = i
        # 最后拉通看一遍最大值
        return max(dp)

思路3:隔板法

Python 代码:

class Solution:
    def lengthOfLongestSubstring(self, s):
        # 特判
        l = len(s)
        if l < 2:
            return l
        # 隔板法
        # key:字符,val 出现的索引
        map = dict()
        point = 0
        res = 1
        for i in range(l):
            # 关键1:map[s[i]] >= point,等于是可以的
            if s[i] in map and map[s[i]] >= point:
                # 先把隔板向后移动一位
                point = map[s[i]] + 1
            # 然后记录最长不重复子串的长度
            res = max(res, i - point + 1)
            # 关键2:无论如何都更新位置
            map[s[i]] = i
        return res

参考资料:LeetCode 第 3 题:最长不重复字符串

第 49 题:丑数

传送门:丑数

把只包含因子 2 2 2 3 3 3 5 5 5 的数称作丑数(Ugly Number)。

例如 6 6 6 8 8 8 都是丑数,但 14 14 14 不是,因为它包含因子 7 7 7

求按从小到大的顺序的第 N N N 个丑数。

样例:

输入:5

输出:5

注意:习惯上我们把 1 1 1 当做是第一个丑数。

同 LeetCode 第 264 题,题解传送门:LeetCode 上的丑数问题

思路:所谓的一个数 m m m 是另一个数 n n n 的因子,是指 n n n 能被 m m m 整除,也就是 n % m = = 0 n\%m==0 n%m==0 成立。根据丑数的定义,丑数只能被 2 2 2 3 3 3 5 5 5 整除。根据丑数的定义,丑数应该是另一个丑数乘以 2 2 2 3 3 3 或者 5 5 5 的结果( 1 1 1除外)。因此我们可以创建一个数组,里面的数字是排好序的丑数,每一个丑数都是前面的丑数乘以 2 2 2 3 3 3 或者 5 5 5 得到的。

这个思路的关键问题在于怎样保证数组里面的丑数是排好序的。对乘以 2 2 2 而言,肯定存在某一个丑数 T 2 T2 T2,排在它之前的每一个丑数乘以 2 2 2 得到的结果都会小于已有最大的丑数,在它之后的每一个丑数乘以乘以 2 2 2 得到的结果都会太大。我们只需要记下这个丑数的位置,同时每次生成新的丑数的时候,去更新这个 T 2 T2 T2。对乘以 3 3 3 5 5 5 而言,也存在着同样的 T 3 T3 T3 T 5 T5 T5

Python 代码:

class Solution:
    def GetUglyNumber_Solution(self, index):
        # write code here
        if index < 7:
            return index
        res = [1, 2, 3, 4, 5, 6]
        t2, t3, t5 = 3, 2, 1
        for i in range(6, index):
            res.append(min(res[t2] * 2, min(res[t3] * 3, res[t5] * 5)))
            while res[t2] * 2 <= res[i]:
                t2 += 1
            while res[t3] * 3 <= res[i]:
                t3 += 1
            while res[t5] * 5 <= res[i]:
                t5 += 1
        return res[index - 1]

第 50-1 题:字符串中第一个只出现一次的字符

传送门:字符串中第一个只出现一次的字符

在字符串中找出第一个只出现一次的字符。

如输入"abaccdeff",则输出b

如果字符串中不存在只出现一次的字符,返回#字符。

样例:

输入:"abaccdeff"

输出:'b'

同 LeetCode 第 387 题,传送门:字符串中的第一个唯一字符

思路:特别容易想到的思路,就是统计词频,统计词频可以用哈希表,也可以用数组。

Python 代码:

class Solution:
    def firstNotRepeatingChar(self, s):
        """
        :type s: str
        :rtype: str
        """
        if len(s) < 1:
            return '#'

        counter = [0 for _ in range(256)]
        for alpha in s:
            counter[ord(alpha)] += 1
        for alpha in s:
            if counter[ord(alpha)] == 1:
                return alpha
        # 要注意:如果是 "aabbcc" 这种所有的字符都出现不止 1 次,
        # 就按照题意,返回 '#'
        return '#'

第 50-2 题:字符流中第一个不重复的字符

传送门:字符流中第一个只出现一次的字符

请实现一个函数用来找出字符流中第一个只出现一次的字符。

例如,当从字符流中只读出前两个字符”go”时,第一个只出现一次的字符是’g’。

当从该字符流中读出前六个字符”google”时,第一个只出现一次的字符是’l’。

如果当前字符流没有存在出现一次的字符,返回#字符。

样例:

输入:"google"

输出:"ggg#ll"

解释:每当字符流读入一个字符,就进行一次判断并输出当前的第一个只出现一次的字符。

Python 代码:使用辅助数组,做字母频数统计

class Solution:

    def __init__(self):
        self.chars = [0 for _ in range(256)]
        self.strs = []

    def firstAppearingOnce(self):
        """
        :rtype: str
        """
        for char in self.strs:
            if self.chars[ord(char)] == 1:
                return char
        return '#'

    def insert(self, char):
        """
        :type char: str
        :rtype: void
        """
        self.chars[ord(char)] += 1
        self.strs.append(char)

Java 代码:

image-20190107111906223

Python 代码:

image-20190117021201467

Python 代码:

class Solution:
    h = {}
    r = []
    def firstAppearingOnce(self):
        while len(Solution.r):
            if Solution.h[Solution.r[0]] == 1:
                return Solution.r[0]
            Solution.r = Solution.r[1:]
        return '#'

    def insert(self, char):
        Solution.h[char] = Solution.h.get(char, 0) + 1
        if Solution.h[char] == 1:
            Solution.r.append(char)

作者:applezjm
链接:https://www.acwing.com/activity/content/code/content/19320/
来源:AcWing
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

(本节完)

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值