力扣第三十八题-外观数列

708 阅读3分钟

这是我参与8月更文挑战的第5天,活动详情查看:8月更文挑战

前言

力扣第三十八题 外观数列 如下所示:

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

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

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

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

image.png

示例 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"

一、思路

这一题题目特别长,需要多看两遍才能明白是什么意思。这一题主要有一个比较重要的信息:

  • 外观数列的定义:递归公式定义的数字字符串序列

因为 n 对应的数列是对 n-1 对应的外观数列的描述,所以很明显我们可以通过递归来生成 n 对应的外观数列。

但是如何通过 n-1 对应的外观数列生成 n 对应的外观数列呢?

举个例子

我们可以通过下面这个例子来具象化:已知 n=4 对应的外观数列为 1211,那么 n=5 对应的外观数列是什么呢?

tips:我们知道 n=5 对应的外观数列是对 n=4 的描述,在描述中需要将相同的值连读。比如 1211 中有两个连续的 1 需要读做 两个 1

大致的思路分为以下几步:

i=0:i为遍历的下标

  1. 从左至右遍历 n=4 的外观数列 1211只要相邻的两个值不相等就记录结果
  2. i = 0 时,拿到字符串 1
  3. i = 1 时,拿到字符串 2。此时相邻的两个值不相同记录结果 一个 1
  4. i = 2 时,拿到字符串 1。此吃相邻的两个值不相同记录结果 一个 2
  5. i = 3 时,拿到字符串 1。此吃相邻的值相同,但为最后一个字符串,也许记录结果 两个 1
  6. 最终结果为 一个1 一个2 两个 1,即111221

上面这个遍历过程用双指针可以有效的减少存储一个字符串的空间,所以在实现中就是使用双指针+递归来做的。.

二、实现

实现代码

实现代码与思路中保持一致

public String countAndSay(int n) {
    if (n == 1)
        return "1";
    else {
        String val = countAndSay(n -1);
        return explain(val);
    }
}

/**
 * 双指针:描述某个值
 */
public String explain(String val) {
    StringBuilder sb = new StringBuilder();
    char[] arr = val.toCharArray();
    int left = 0;
    int right = 0;
    while (right < arr.length) {
        // 不相等
        if (arr[left] != arr[right]) {
            sb.append(right - left).append(arr[left]);
            // 移动左指针
            left = right;
        }
        // 是否为最后一个元素
        if (right == arr.length -1) {
            sb.append(right - left + 1).append(arr[left]);
        }
        right ++;
    }
    return sb.toString();
}

测试代码

public static void main(String[] args) {
    String ret = new Number38().countAndSay(5);
    System.out.println(ret);
}

结果

image.png

三、总结

感谢看到最后,非常荣幸能够帮助到你~♥