思路
这道题本身不难, 但是题目有点难理解, 我也是捉摸了很久才看懂他题目什么意思. 其实它第二个输出的numRows
就是决定字符串以ZigZag摆放形式的最大行数. 具体可以去网上查一下, 这里我不多提. 我个人想到的思路大概是这样的 :
- 将这个字符串摆放出来的图形看成一个数组, 那么每个点的坐标为(i, j).
- 如果 j % (numRows - 1) == 0的话, 那么这一列是满的(但是如果这一列是最后一列也不一定), 那么我们只需要找到这一列前面字母的数量, 就能知道这一列的当前字母的坐标.
- 如果j % (numRows - 1) != 0, 说明这一列上只有一个点是合法的, 其他点实际都不存在, 那么哪个点合法呢? 根据这个图形的特点可以看出来, 离前一个满列越近, i就应该越低, 最靠近的时候应该出现在numRows - 1 行, 也就是 i 应该等于 numRows - 2.
- 这时候只需要横向遍历这个图形, 判断该点是否合法, 合法的再求出这个点之前出现的字符串的数量然后插入要返回的字符串当中.
实现
这道题corner case贼多, 我踩了几个坑.
- 字符串为空.
- numRows为0 或者 1, 此时字符串保持不变.
- 由于是一行一行遍历, 还有一种不合法的情况在于, 我们遍历的位置逻辑上合法, 但是实际到这里字符串已经结束了, 这里也需要额外注意.
提交
前三次都是爆炸异常, 第三次才AC.
代码
public class Solution {
public static void main(String[] args) {
System.out.println(new Solution().convert("PAYPALISHIRING", 4));
}
public String convert(String s, int numRows) {
int length = s.length();
if(length == 0) return "";
if(numRows < 2) return s;
int oneCircleSize = numRows + numRows - 2;
int numColumns = (length / oneCircleSize) * (numRows - 1);
numColumns += length % oneCircleSize / numRows > 0 ? length % oneCircleSize % numRows + 1 : 1;
StringBuffer stringBuffer = new StringBuffer();
for(int i = 0; i < numRows; ++i){
for(int j = 0; j < numColumns; ++j){
if(j % (numRows - 1) == 0){
int index = oneCircleSize * (j / (numRows - 1)) + i;
if(index >= length) continue;
stringBuffer.append(s.charAt(index));
}
else if(numRows - j % (numRows - 1) == i + 1){
int index = oneCircleSize * (j / (numRows - 1)) + numRows + j % (numRows - 1) - 1;
if(index >= length) continue;
stringBuffer.append(s.charAt(index));
}
}
}
return stringBuffer.toString();
}
}
最佳实现
上面的解法我个人感觉还是可以有所优化的, 就是说其实没有必要便利整个二维数组, 因为我们只要拿到当前位置完全可以计算出下一个位置的点, 于是我对自己的方法进行了一波改进 :
public class Solution {
public static void main(String[] args) {
//System.out.println(new Solution().convert("PAYPALISHIRING", 4));
}
public String convert(String s, int numRows) {
if(numRows < 2) return s;
int length = s.length();
if(length == 0) return "";
int oneCircleSize = numRows + numRows - 2;
int numColumns = (length / oneCircleSize) * (numRows - 1);
numColumns += length % oneCircleSize / numRows > 0 ? length % oneCircleSize % numRows + 1 : 1;
StringBuffer stringBuffer = new StringBuffer();
int row = 0;
int column = 0;
while (true){
if(length == stringBuffer.length()) break;
if(column % (numRows - 1) == 0){
int index = oneCircleSize * (column / (numRows - 1)) + row;
if(index < length){
stringBuffer.append(s.charAt(index));
}
if(row == 0 || row == numRows - 1){
column += numRows - 1;
}else{
column += numRows - 1 - row;
}
if(column > numColumns - 1){
column = 0;
++row;
}
}
else{
int index = oneCircleSize * (column / (numRows - 1)) + numRows + column % (numRows - 1) - 1;
stringBuffer.append(s.charAt(index));
column = (column + numRows - 1) / (numRows - 1) * (numRows - 1);
if(column > numColumns - 1){
column = 0;
++row;
}
}
}
return stringBuffer.toString();
}
}
时间排名从30%跳到了87%, 我个人感觉这应该算是最佳的方式了...