每天 3 分鐘,走上算法的逆襲之路。
前文合集
代碼倉庫
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();
}
這個答案只能說僅供參考吧,完全不知道是怎麼推導出來的。這道題主要是要記住第一個解題方案中的逆向迭代的方案,可以加一個轉向標記位。