题目
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();
}
}