leetcode 精选top面试题 - 38. 外观数列

38. 外观数列

给定一个正整数 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" 的描述如下图:

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

思路一:

简单模拟,从第一项开始,计算出当前数字的外观字符串,然后一直迭代,直到第n项。

每一项的计算过程为:

遍历字符串,统计连续字符出现的次数
  • 如果当前字符等于后一个字符,计数器加一
  • 否则结果串中保存结果,计数器重置,临时字符重置
  • 当前循环结束后把最后一组重复数字的出现次数和数字附加到结果串中
  • 把当前外观字符串赋值为下个用来迭代的原字符串
 1 class Solution {
 2     public String countAndSay(int n) {
 3         if(n == 1){
 4             return "1";
 5         }
 6         // 把整数n转换成字符串
 7         String str = "1";
 8         StringBuilder sb = new StringBuilder(); // 结果串
 9         for(int i = 1; i < n; i++){
10             // 遍历字符串,统计连续字符出现的次数
11             int len = str.length();            
12             char ch = str.charAt(0);
13             int cnt = 1;
14             for(int j = 1; j < len; ++j){
15                 // 如果当前字符等于后一个字符,计数器加一
16                 if(str.charAt(j) == ch){
17                     cnt++;
18                 }else{ // 否则结果串中保存结果,计数器重置,临时字符重置
19                     sb.append(cnt).append(ch);
20                     cnt = 1;
21                     ch = str.charAt(j);
22                 } 
23             }
24             // 把最后一组重复数字的出现次数和数字附加到结果串中
25             sb.append(cnt).append(ch);
26             str = sb.toString();
27             sb = new StringBuilder();     
28         }
29         return str;
30     }
31 }

leetcode 执行用时:6 ms, 在所有 Java 提交中击败了35.18%的用户

  内存消耗:36.1 MB, 在所有 Java 提交中击败了64.99%的用户

复杂度分析:

时间复杂度:双重循环,外层循环迭代次数为n, 内层循环迭代次数等于字符串的长度,但是字符串长度每次都不一样,但是不会太长,每次变长之后会被拉回来,复杂度不太好判断。但是假设字符串最长为100,那时间复杂度就是O(100n)
空间复杂度:空间复杂度和时间复杂度类似,也不太好判断,但是假设字符串最长为100,那空间复杂度就是O(100n)

思路二:

整体思路和思路一一样,但是内层循环通过双指针来找到重复数字出现的次数
 1 class Solution {
 2     public String countAndSay(int n) {
 3         if(n == 1){
 4             return "1";
 5         }
 6 
 7         // 把整数n转换成字符串
 8         String str = "1";
 9         StringBuilder sb = new StringBuilder(); // 结果串
10         for(int i = 1; i < n; i++){
11             // 遍历字符串,统计连续字符出现的次数
12             int len = str.length();  
13             int start = 0, end = 0;     // 利用双指针
14             while(end < len){
15                 if(str.charAt(start) == str.charAt(end)){
16                     end++;
17                 }else{
18                     sb.append(end - start).append(str.charAt(start));
19                     start = end;    // 重置start指针,重新开始统计下个数字的出现次数
20                 }
21             }
22             // 把最后一组重复数字的出现次数和数字附加到结果串中
23             sb.append(end - start).append(str.charAt(start));
24             str = sb.toString();
25             sb = new StringBuilder();     
26         }
27         return str;
28     }
29 }

leetcode 执行用时:6 ms, 在所有 Java 提交中击败了35.18%的用户

    内存消耗:36 MB, 在所有 Java 提交中击败了73.11%的用户
posted @ 2020-12-04 20:35  Lucky小黄人^_^  阅读(156)  评论(0编辑  收藏  举报