題目:將字符串按 Z 字形(N 行)排列後按行讀取生成新字符串。
難度:Easy
示例:"PAYPALISHIRING", 3 行 → "PAHNAPLSIIGYIR"
Z 字形軌跡呈現固定週期:下行 → 右上斜行 → 下行 → ...
週期長度 cycle = 2 * (numRows - 1)(首尾行無斜行部分)。
0, cycle, 2*cycle, ... → 間隔 cyclenumRows-1, numRows-1+cycle, ... → 間隔 cycledown = 2 * (numRows - 1 - i)up = 2 * iclass Solution {
public String convert(String s, int numRows) {
if (numRows <= 1 || s.length() <= numRows) return s;
StringBuilder sb = new StringBuilder();
int n = s.length();
int cycle = 2 * (numRows - 1);
for (int row = 0; row < numRows; row++) {
int down = 2 * (numRows - 1 - row);
int up = 2 * row;
boolean goDown = true; // 方向標誌
for (int i = row; i < n; ) {
sb.append(s.charAt(i));
if (row == 0 || row == numRows - 1) {
i += cycle; // 首尾行固定間隔
} else {
i += goDown ? down : up; // 中間行交替間隔
goDown = !goDown; // 翻轉方向
}
}
}
return sb.toString();
}
}
| 細節 | 處理方式 |
|---|---|
| numRows = 1 | 直接返回原串,避免 cycle = 0 導致死循環 |
| 字符串長度 ≤ numRows | 無法形成 Z 形,直接返回 |
| 中間行方向控制 | boolean 旗標 + 三元運算符,無需額外數組存儲軌跡 |
| StringBuilder | 線程不安全但高效,單線程拼接首選 |
// 缺點:O(n * numRows) 空間,需遍歷矩陣讀取,邊界判斷繁瑣
char[][] matrix = new char[numRows][n];
int row = 0, col = 0, dir = 1; // 1向下, -1向上
for (char c : s.toCharArray()) {
matrix[row][col++] = c;
if (row == 0) dir = 1;
else if (row == numRows - 1) dir = -1;
row += dir;
}
// 再按行讀取非空字符...
@Test
public void test() {
Solution sol = new Solution();
assertEquals("PAHNAPLSIIGYIR", sol.convert("PAYPALISHIRING", 3));
assertEquals("PINALSIGYAHRPI", sol.convert("PAYPALISHIRING", 4));
assertEquals("A", sol.convert("A", 1));
assertEquals("ABCD", sol.convert("ABCD", 5)); // 行數超長度
assertEquals("AB", sol.convert("AB", 1));
}
若字符串極長(GB 級),可改為生成器模式逐行輸出,避免全量內存佔用:
public StreamconvertStream(Reader reader, int numRows) { // 使用環形緩衝區或臨時文件分行緩存,逐行 yield // 核心索引計算邏輯不變,僅將 sb.append 改為寫入行緩衝 }
「Z 字形變換本質是週期性索引映射問題。掌握『首尾固定間隔、中間交替間隔』規律,即可用 O(1) 額外空間(不計輸出)單趟掃描解決,這是字符串模擬類題目的通用解題模板。」