每日一道 LeetCode (11):外觀數列
每天 3 分鐘,走上演算法的逆襲之路。
前文合集
程式碼倉庫
GitHub: https://github.com/meteor1993/LeetCode
Gitee: https://gitee.com/inwsy/LeetCode
題目:外觀數列
題目來源:https://leetcode-cn.com/problems/count-and-say/
給定一個正整數 n(1 ≤ n ≤ 30),輸出外觀數列的第 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
示例 1:
輸入: 1
輸出: "1"
解釋:這是一個基本樣例。
示例 2:
輸入: 4 輸出: "1211" 解釋:當 n = 3 時,序列是 "21",其中我們有 "2" 和 "1" 兩組,"2" 可以讀作 "12",也就是出現頻次 = 1 而 值 = 2;類似 "1" 可以讀作 "11"。所以答案是 "12" 和 "11" 組合在一起,也就是 "1211"。
解題思路
今天下班回來太晚了,我就簡單寫寫,有不理解的再私信問我吧。
首先幫忙理解下這道題,這道題的意思實際上就是按照一種規律來進行描述數字。
按照正常人的思路,這個演算法首先需要一個大迴圈,直接迴圈到 n ,然後在每一次迴圈中得到上一次結果的新的描述。
- 最開始第一次是 1 , 這個是規定。
- 第二次迴圈是描述上一個數字,結果是 11 ,意思是上一個數字是 1 個 1 。
- 第三次迴圈上描述上一次的 11 ,結果是 21 ,含義是 2 個 1 。
- 第四次迴圈還是描述上一次的 21 ,結果是 1211 ,含義是 1 個 2 , 1 個 1 。
- ......
後面的我就不寫了,這道題就先拆解到這裡,確實蠻難理解的,說實話,我看題是沒看懂的,直到我看到了答案分析了程式碼後才看懂題。
我這是真的菜的一批。
解題
首先宣告,這道題我沒有想出來任何解法,所有解法均來自於 LeetCode 的網友。
最皮的解法
第一個我一定要介紹這個解法,因為這個題給出的數字是有限的,所以有一個非常皮的網友直接給了這麼一串程式碼,看的我是目瞪口呆:
public String countAndSay(int n) {
switch (n) {
case 1:
return "1";
case 2:
return "11";
case 3:
return "21";
case 4:
return "1211";
case 5:
return "111221";
case 6:
return "312211";
case 7:
// 中簡程式碼省略,太長了
...
default:
return "0";
}
}
這位同學抓住了這道題的輸入是有限制的,直接把所有的結果窮舉出來,然後用了一波 switch
,我是真的服。
果然刷 LeetCode 題的人的腦回路和正常人完全不同。
常規迭代解法
對於題還沒理解清楚的同學可以多 debug 幾次下面這段程式碼,這種方案是一個人的常規正向的思維過程。
public static String countAndSay(int n) {
if ( n == 1 ) return "1";
String s_n_1 = "1";
// 首先外層先迴圈到 n ,從 2 開始。
for ( int i = 2; i <= n; i++ ) {
StringBuilder sb = new StringBuilder();
int pre = 0;// 前驅節點
int j = 0;
// 開始描述上一次得到的數字
for ( j = 0; j < s_n_1.length(); j++ ) {
if ( s_n_1.charAt(j)!=s_n_1.charAt(pre) ){
sb.append(j-pre).append(s_n_1.charAt(pre));
pre = j;
}
}
sb.append(j-pre).append(s_n_1.charAt(pre));
s_n_1 = sb.toString();
}
return s_n_1;
}
不好寫的遞迴方案
遞迴講道理我還是不大會寫,硬著頭皮先抄一遍程式碼吧。
public String countAndSay_1(int n) {
if (n == 1) return "1";
StringBuilder res = new StringBuilder();
String str = countAndSay_1(n - 1);
int length = str.length();
int a = 0;
for (int i = 1; i < length + 1; i++) {
if (i == length) {
return res.append(i - a).append(str.charAt(a)).toString();
} else if (str.charAt(i) != str.charAt(a) ) {
res.append(i - a).append(str.charAt(a));
a = i;
}
}
return res.toString();
}
先這樣,今天晚上時間比較緊,寫的內容不多,還望各位看到這篇文章的同學海涵,明天我一定找補回來。