每日一道 LeetCode (49):Z 字形變換

每天 3 分鐘,走上算法的逆襲之路。

前文合集

每日一道 LeetCode 前文合集

代碼倉庫

GitHub: https://github.com/meteor1993/LeetCode

Gitee: https://gitee.com/inwsy/LeetCode

題目:最長迴文子串

難度:中等

題目來源:https://leetcode-cn.com/problems/zigzag-conversion/

難度:中等

將一個給定字符串根據給定的行數,以從上往下、從左到右進行 Z 字形排列。

比如輸入字符串爲 "LEETCODEISHIRING" 行數爲 3 時,排列如下:

L   C   I   R
E T O E S I I G
E   D   H   N

之後,你的輸出需要從左往右逐行讀取,產生出一個新的字符串,比如:"LCIRETOESIIGEDHN"。

請你實現這個將字符串進行指定行數變換的函數:

string convert(string s, int numRows);

示例 1:

輸入: s = "LEETCODEISHIRING", numRows = 3
輸出: "LCIRETOESIIGEDHN"

示例 2:

輸入: s = "LEETCODEISHIRING", numRows = 4
輸出: "LDREOEIIECIHNTSG"
解釋:

L     D     R
E   O E   I I
E C   I H   N
T     S     G

解題方案一:按行排序

這種題就是那種點型的一看不會做,一看答案感覺自己是怕不是個智障。

最開始的時候想着使用純暴力的方案去排那個字符串,但是這個字符串由於是 Z 字型的,那就需要用到二維數組,愣是沒想通先豎着排完以後如何轉向的問題。

不得已之下去看了答案,看完以後恨不得抽自己倆大嘴巴子,直接用正負 1 來控制輸出的轉向就這麼難想麼?

哎。。。感覺別人的腦子是個腦子,自己的腦子可能它只是掛了個名字。。。

答案中並沒有選擇使用數組,而是使用了一個 List<StringBuilder> 的集合,也算是達到了一個二維數組的概念。

public String convert(String s, int numRows) {
    // 極限值判斷
    if (numRows == 1) return s;
    // 定義行
    List<StringBuilder> rows = new ArrayList<>();
    // 每一行都增加一個列
    for (int i = 0; i < Math.min(numRows, s.length()); i++)
        rows.add(new StringBuilder());
    // 定義當前行數
    int curRow = 0;
    // 定義是否轉向
    boolean goingDown = false;

    for (char c : s.toCharArray()) {
        // 當前行添加一個字符
        rows.get(curRow).append(c);
        // 如果當前行是第一行或者是最後一行,轉向標記位取反
        if (curRow == 0 || curRow == numRows - 1) goingDown = !goingDown;
        // 注意:這裏計算了下一次循環的行數,並且完成了循環中轉向
        curRow += goingDown ? 1 : -1;
    }
    // 定義輸出
    StringBuilder ret = new StringBuilder();
    for (StringBuilder row : rows) ret.append(row);
    return ret.toString();
}

代碼我就不解釋了,每一行的註釋都加好了,這個循環中轉向的思路我以後絕對會記住的,簡直是奇恥大辱。

解題方案二:按行訪問

這個方案絕對是 NB 的大神拿個紙和筆坐在那活活推導出來的結果,就一個字,服!

這個方案屬於那種直接寫結果的方案,直接按照逐行讀取讀取的順序來輸出整個字符串。

這我還能說啥,知道了公式以後不就是照着公式寫代碼嘛,不知道公式就只能乾瞪眼。

其實我在剛看到這道題的時候就想,這個通項公式肯定存在,以我的能力估計是推導不出來,這個方案直接 pass 掉了。

果然在答案中是能人輩出,直接給出通項公式:

  • 對於所有整數 k
  • 行 0 中的字符位於索引 k (2 * numRows - 2) 處。
  • 行 numRows - 1 中的字符位於索引 k (2 * numRows - 2) + numRows - 1 處。
  • 內部的行 i 中的字符位於索引 k (2 * numRows - 2) + i 以及 (k + 1)(2 * numRows - 2) - i 處。
public String convert_1(String s, int numRows) {
    if (numRows == 1) return s;
    // 定義輸出
    StringBuilder ret = new StringBuilder();
    int n = s.length();
    int cycleLen = 2 * numRows - 2;

    for (int i = 0; i < numRows; i++) {
        for (int j = 0; j + i < n; j += cycleLen) {
            ret.append(s.charAt(j + i));
            if (i != 0 && i != numRows - 1 && j + cycleLen - i < n)
                ret.append(s.charAt(j + cycleLen - i));
        }
    }

    return ret.toString();
}

這個答案只能說僅供參考吧,完全不知道是怎麼推導出來的。這道題主要是要記住第一個解題方案中的逆向迭代的方案,可以加一個轉向標記位。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章