題目
6. ZigZag Conversion
The string “PAYPALISHIRING” is written in a zigzag pattern on a given number of rows like this: (you may want to display this pattern in a fixed font for better legibility)
P A H N
A P L S I I G
Y I R
And then read line by line: “PAHNAPLSIIGYIR”
Write the code that will take a string and make this conversion given a number of rows:
string convert(string s, int numRows);
將一個給定字符串根據給定的行數,以從上往下、從左到右進行 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);
分析
依據題意,此題最關鍵的顯然就是找出所有字符所在的行數,這樣才能以行的順序將新的字符串返回。
爲了找出字母與行之間的關係,我們需要發掘一些規律:
思路一:移動方向
動態考慮Z型字符串在豎直方向上的產生過程,比如:
就是先從P開始,以↓表示向下走,以↑表示向上走,即爲P↓A↓Y↑P↑A↓L↓I……
不難發現箭頭的朝向是有規律的:
只有在字符到達第一行(curRow==1
)或最後一行(curRow==numRows
)時,方向纔會發生切換。
思路二:靜態分組
我們不考慮Z字型字符串的產生過程,直接考慮其最終結果,將之分組。
也就是每次當字符重新回到第一行時,會產生新的一組。
這樣分組以後,我們只需要知道每個字母在它所在的那組中的位置,就可以得到它的行數了。因爲每組的結構都是完全一樣的(都是兩列,且左列numRows
行,右列numRows - 2
行)。
而除了最後一組以外,每組的個數是groupNum = (2 * numRows - 2)
(numRows > 1時才成立)。
在分組以後,對於每組,我們不難發現:(假設當前行爲 i,行從0開始數)
i == 0
或i == numRows-1
時(也就是第一行和最後一行),該組內都只有一個字符滿足。
i
爲其他值時,該組內總有兩個字符滿足。這兩個字符在組中的位置分別是 i 和 groupNum - i。
綜上所述,我們只需先將原先的字符串 s 進行分組,然後對於組進行遍歷,就可以得到每行的字符串了。
題解
Java實現思路一
import java.util.ArrayList;
import java.util.List;
class Solution {
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();
}
}
C++實現思路一
class Solution {
public:
string convert(string s, int numRows) {
if (numRows == 1) return s;
vector<string> rows (min(numRows, int(s.size())));
int curRow = 0;
bool goingDown = false;
for (char c : s) {
rows[curRow] += c;
if (curRow == 0 || curRow == numRows - 1) goingDown = !goingDown;
curRow += goingDown ? 1 : -1;
}
string ret;
for (string row : rows) ret += row;
return ret;
}
};
以上兩個都是LeetCode的官方題解。
Java實現思路二
import java.util.ArrayList;
import java.util.List;
public class Solution {
public String convert(String s, int numRows) {
StringBuilder returnString = new StringBuilder();
if(numRows == 1)return s;
int groupNum = numRows * 2 - 2;//一組的個數
List<String> groups = new ArrayList<>();
for(int i=0;i<s.length();i+=groupNum){//分組
if(i + groupNum <= s.length()) {
groups.add(s.substring(i, i + groupNum));
}else{
groups.add(s.substring(i,s.length()));
}
}
for(int i=0;i<numRows;i++){//按行輸出,從第一行到最後一行
for(String group:groups){
if(i == 0){
//打印該組第一行(僅一個字符)
returnString.append(group.charAt(0));
}
else if(i == numRows - 1){
//打印該組最後一行(僅一個字符)
if(numRows - 1 < group.length()) {
returnString.append(group.charAt(numRows - 1));
}
}else{
//打印該組其他行(兩個字符,index分別是i和groupNum-i)
if(i < group.length()) {//確保數組訪問不越界(也就是考慮到最後一組的特殊情況)
returnString.append(group.charAt(i));
}
if(groupNum-i < group.length()) {
returnString.append(group.charAt(groupNum-i));
}
}
}
}
return returnString.toString();
}
}