public class LeetCode6 {
public static void main(String[] args) {
/**
* LeetCode 第6题:将字符串 "PAYPALISHIRING" 以Z字形排列成给定的行数:
P A H N
A P L S I I G
Y I R
之后从左往右,逐行读取字符:"PAHNAPLSIIGYIR"
样例: 输入: s = "PAYPALISHIRING", numRows = 4
输出: "PINALSIGYAHRPI"
解释:
P I N
A L S I G
Y A H R
P I
*解题思路:要得到解释后的二维数组 conStr[numRows][len]; len未知?
* 找规律, len = s.length()/ (2*numRows - 2) * (numRows - 1);
* 解释一下将一个有完整列的如:PAYP这一列到ISHI这一列,有 numRows - 2个字符,加上完整的一列则为 2*numRows -2个字符
* 而这种规律递进的行长为 numRows - 1, 当然 len != s.length() / 2; 计算机运算取整!!
* 当然还有不规律的,如最后的NG这一列,其判读 otherstr = s.length()% (2*numRows - 2)
* 判断他的列数进行相加得到最后的len
* 那么接下来就填充再取出就好了
*/
String s = "PAYPALISHIRING";
System.out.println(convert(s,9));
}
public static String convert(String s, int numRows) {
if(numRows == 1 )
return s;
int len = s.length() / (2*numRows - 2) * (numRows - 1);
int otherStr = s.length() % (2*numRows - 2),otherLen = 0;
if(otherStr != 0 ){
if(otherStr <= numRows)
otherLen = 1;
else
otherLen = otherStr - numRows + 1;
}
len = len + otherLen;
char conStr[][] = new char [numRows][len];
int k = 0 , l ,i = 0,j = 0;
while(k < s.length()){
//填充完整的一列
i = 0; l = numRows;
while(l > 0 && k < s.length()){
conStr[i][j] = s.charAt(k);
i++; k++; l--;
}
//像右推进一列,行数回归到非完整的起始行
j++; i = numRows - 2;
//填充非完整的列
for(int z = numRows - 2;z > 0;z--){
if(k < s.length()){
conStr[i][j] = s.charAt(k);
i--; j++; k++;
}
}
}
StringBuilder changedStr = new StringBuilder() ;
for( i = 0 ; i < numRows ; i++){
for( j = 0 ; j < len ; j++){
if(conStr[i][j] != '\0'){
changedStr.append(conStr[i][j]);
}
}
}
return new String(changedStr);
}
}
事实上,根本不需要二维数组,采用 List<StringBuilder> rows = new ArrayList<>();
将每行的字符记录,当到了最上面的行或者最下面的行才更改到下一列(假设你是看了上面二维数组排序的),那如何在List中实现呢?在List中的实现原理就是移动方向,用 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;
}
行数curRow为min(s.length(),numRows);,开始curRow为0,goingDown变为true,记录到最下面的行,转变方向,直到记录完全。
具体方法:
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();
}
当然第一种方法是暴力的,其实可以更暴力点,就是用那种思想来直接进行字符的检索,不使用二维数组,只是不太好理解。
具体代码:
class Solution {
public String convert(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();
}
}