leetcode题记:Count and Say

编程语言:JAVA

提交结果:

Submission Detail
18 / 18 test cases passed.
Status: Accepted
Runtime: 41 ms
Submitted: 0 minutes ago
You are here! 
Your runtime beats 4.27 % of java submissions

题目描述:

The count-and-say sequence is the sequence of integers with the first five terms as following:

1.     1
2.     11
3.     21
4.     1211
5.     111221
1 is read off as "one 1" or 11.
11 is read off as "two 1s" or 21.
21 is read off as "one 2, then one 1" or 1211.
Given an integer n, generate the nth term of the count-and-say sequence.

Note: Each term of the sequence of integers will be represented as a string.

Example 1:

Input: 1
Output: "1"
Example 2:

Input: 4
Output: "1211"

这道题的题目比较不好理解,解释如下:

解释一下就是,输入n,那么我就打出第n行的字符串。
怎么确定第n行字符串呢?他的这个是有规律的。

 n = 1时,打印一个1。

 n = 2时,看n=1那一行,念:1个1,所以打印:11。

 n = 3时,看n=2那一行,念:2个1,所以打印:21。

 n = 4时,看n=3那一行,念:一个2一个1,所以打印:1211。

以此类推。(注意这里n是从1开始的)

所以构建当前行的字符串要依据上一行的字符串。“小陷阱就是跑完循环之后记得把最后一个字符也加上,因为之前只是计数而已。”

解题思路:

首先根据博主的第一思路,确定这道题用递归法;然后就是具体操作,对每个字符串统计了;
在对每一个字符串统计的时候,博主建议设置一个哨兵(如果不知道哨兵,建议百度一下”哨兵数据结构“这几个关键词),每次与这个哨兵比较。根据哨兵的使用习惯,给哨兵赋值为a[0]。
博主理解:设置哨兵会使比较时候的思路变得清晰,建议多理解哨兵的思想。

伪代码:

if n == 1: return "1";

array = countAndsay(n-1);

key = array[0];//key为哨兵

count = 0//count为哨兵计数

s = "";//s为空字符串

循环,从0到n-1if array[i]==key:

        count += 1;

    else:

        s += count;

        s += key;

        count = 1;

        key = array[i];

s += count;

s += key;

return s;

代码:

class Solution {
    public String countAndSay(int n) {
        if(n==1){
            return "1";
        }
        String tmp = countAndSay(n-1);
        String s = "";
        char[] arr = tmp.toCharArray();
        char cc = arr[0];
        int count = 0;

        for(int i = 0;i < arr.length;i++){
            if(arr[i]==cc){
                count += 1;
            }
            else{
                s += count;
                s += cc;

                count = 1;
                cc = arr[i];
            }
        }
        s += count;
        s += cc;

        return s;
    }
}

优化:

对于这道题来说,博主提交的结果虽然正确,但是运行时间41ms,打败了4.27%的对手。说明程序写的比较慢。
改进,将for循环改成while循环,for循环与while速度对比实验,这篇博客里详细的对比了两个实验,博主改为while循环后,结果如下:

18 / 18 test cases passed.
Status: Accepted
Runtime: 33 ms
Submitted: 0 minutes ago
You are here! 
Your runtime beats 8.92 % of java submissions.

运行时间由41ms缩短至33ms,可见while还是比for循环更节省时间。但是运行时间还是很长。

再次优化:

博主这次从leetcode讨论区找了一份运行时间为2ms的答案,这份代码的与博主的代码思路基本相同,也是用了递归,不同的是,存储字符串用的是StringBuild,难道是这个原因导致运行变慢吗?
代码如下:

class Solution {
    public String countAndSay(int n) {
       if (n == 1) {return "1";}
        String temp = countAndSay(n - 1);
        int repeat = 1;
        StringBuilder answer = new StringBuilder();
        int i = 1;
        for(; i < temp.length(); i++){
            if(temp.charAt(i) == temp.charAt(i - 1)) {
                repeat ++;
            } else {
                answer.append(repeat);
                answer.append(temp.charAt(i - 1));
                repeat = 1;
            }
        }
        answer.append(repeat);
        answer.append(temp.charAt(i - 1));               
        return answer.toString();
    }
}

博主将自己的代码改为用StringBuilder之后,速度瞬间回到2ms,于是博主开始百度,发现一个技巧:当程序中需要大量的对某个字符串进行操作时,应该考虑应用StringBuilder类处理该字符串,其设计目的就是针对大量string操作的一种改进办法,避免产生太多的临时对象;而当程序中只是对某个字符串进行一次或几次操作时,采用string类即可。string本身是不可改变的,它只能赋值一次,每一次内容发生改变,都会生成一个新的对象,然后原有的对象引用新的对象,而每一次生成新对象都会对系统性能产生影响,这会降低.NET编译器的工作效率
具体参考见一下链接String与StringBuilder的区别

猜你喜欢

转载自blog.csdn.net/papaaa/article/details/81007279