Leetcode 38. 外观数列

296 阅读3分钟

Leetcode 38. 外观数列

持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第16天,点击查看活动详情

❤️‍欢迎订阅java厂长《LeetCode每日一题》 ❤️‍

1、题目📑

给定一个正整数 n ,输出外观数列的第 n 项。

「外观数列」是一个整数序列,从数字 1 开始,序列中的每一项都是对前一项的描述。

你可以将其视作是由递归公式定义的数字字符串序列:

  • countAndSay(1) = "1"
  • countAndSay(n) 是对 countAndSay(n-1) 的描述,然后转换成另一个数字字符串。

前五项如下:

1.     1
2.     11
3.     21
4.     1211
5.     111221
第一项是数字 1 
描述前一项,这个数是 1 即 “ 一 个 1 ”,记作 "11"
描述前一项,这个数是 11 即 “ 二 个 1 ” ,记作 "21"
描述前一项,这个数是 21 即 “ 一 个 2 + 一 个 1 ” ,记作 "1211"
描述前一项,这个数是 1211 即 “ 一 个 1 + 一 个 2 + 二 个 1 ” ,记作 "111221"

描述 一个数字字符串,首先要将字符串分割为 最小 数量的组,每个组都由连续的最多 相同字符 组成。然后对于每个组,先描述字符的数量,然后描述字符,形成一个描述组。要将描述转换为数字字符串,先将每组中的字符数量用数字替换,再将所有描述组连接起来。

例如,数字字符串 "3322251" 的描述如下图:

img

实例1

输入:n = 1

输出:"1"

解释:这是一个基本样例。

实例2

输入:n = 4

输出:"1211"

解释

countAndSay(1) = "1"

countAndSay(2) = 读 "1" = 一 个 1 = "11"

countAndSay(3) = 读 "11" = 二 个 1 = "21"

countAndSay(4) = 读 "21" = 一 个 2 + 一 个 1 = "12" + "11" = "1211"

提示

  • 1 <= n <= 30

2、思路🧠

方法一:字符串模拟

本题的难点在于有些小伙伴在看了题目之后没有明白题目的意图💔。那让厂长简单和大家解释一下。

  • 1
  • 2 描述的是1,是一个1,也就是11
  • 3 描述的是11,是两个1,也就是21
  • 4 描述的是21,是一个2一个1,也就是12-11
  • 5 描述的是1211, 是一个1,一个2,两个1,也就是11-12-21
  • 6 描述的是111221,是三个1,两个2,一个1,也就是31-22-11
  • 7 描述的是312211,是一个3一个1两个2两个1,也即是13-11-22-21
  • 以此类推

递归实现:

根据此题目会想到一个经典的递归题目的公式 F(n) = F(n - 1) ,此题目和这个公式相似,只是解法不同。

  • 分析题目得到递归的终止条件为 n == 1

  • n > 1时,需要拿到 countAndSay(n - 1) 的字符串,并对字符串中每个数字的个数计算:

    • 对于计算字符串中每个数字出现的个数,定义一个变量 count 进行统计。
    • 通过for循环来遍历每个字符串中所有的数字,这里要注意每次统计完一组数字就要 清1 的操作
    • 通过while循环计算字符串中每个数字出现的个数,以及当前计算的数字是什么。
    • 最后通过StringnBuffer对其进行拼接处理,得到结果。

方法二:字符串模拟➕双指针

解法与方法一类似,在判断字符串重复字符的时候巧用了双指针来计算当前数的个数。

废话少说~~~~~上代码!

3、代码👨‍💻

第一次commit AC

class Solution {
    public String countAndSay(int n) {
        if(n == 1) return "1";
        String num = countAndSay(n - 1);
        StringBuffer sb = new StringBuffer();
        for (int i = 0; i <= num.length() - 1; i++) {
            int count = 1;
            while(i < num.length() - 1 && num.charAt(i) == num.charAt(i + 1)) {
                count++;
                i++;
            }
            sb.append(count);
            sb.append(num.charAt(i));
        }
        return sb.toString();
    }
}

时间复杂度:O(N2)

空间复杂度:O(N)

第二次commit AC

class Solution {
    public String countAndSay(int n) {
        if(n == 1) return "1";
        String num = countAndSay(n - 1);
        StringBuffer sb = new StringBuffer();
        int i = 0, j = 1, len = num.length();
        while(j < len) {
            if (num.charAt(i) != num.charAt(j)) {
                sb.append(j - i).append(num.charAt(i));
                i = j;
            }
            j++;
        }
        sb.append(j - i).append(num.charAt(i));
        return sb.toString();
    }
}

image-20220610113614454

4、总结

该题目的对思维的分析与探讨,对于题目的解读能力是非常关键的,在力扣的刷题过程中,字符串的问题还是比较多的,在解决字符串的问题要灵活分析应用合适的方法进行拆解并提交。

❤️‍来自专栏《LeetCode基础算法题》欢迎订阅❤️‍

厂长写博客目的初衷很简单,希望大家在学习的过程中少走弯路,多学一些东西,对自己有帮助的留下你的赞赞👍或者关注➕都是对我最大的支持,你的关注和点赞给厂长每天更文的动力。

对文章其中一部分不理解,都可以评论区回复我,我们来一起讨论,共同学习,一起进步!

原题链接:38. 外观数列 - 力扣(LeetCode)