题目
将一个给定字符串 s 根据给定的行数 numRows ,以从上往下、从左到右进行 Z 字形排列。
比如输入字符串为 "PAYPALISHIRING" 行数为 3 时,排列如下:
P A H N A P L S I I G Y I R
之后,你的输出需要从左往右逐行读取,产生出一个新的字符串,比如:"PAHNAPLSIIGYIR"。
请你实现这个将字符串进行指定行数变换的函数:
string convert(string s, int numRows);
示例 1:
输入:s = "PAYPALISHIRING", numRows = 3
输出:"PAHNAPLSIIGYIR"
示例 2:
输入:s = "PAYPALISHIRING", numRows = 4
输出:"PINALSIGYAHRPI"
解释:
P I N A L S I G Y A H R P I
示例 3:
输入:s = "A", numRows = 1
输出:"A"
提示:
1 <= s.length <= 1000
s 由英文字母(小写和大写)、',' 和 '.' 组成
1 <= numRows <= 1000
思路
模拟
题目的操作并不复杂,可以直接按照题目意思直接模拟,将字符串转化成一个矩阵,然后再遍历矩阵即可得到最后的字符串。但是这种方式提交很容易超时,而且构建出的矩阵有很大部分都是浪费掉了。
直接构造
相对于模拟得到最后的矩阵,其实我们并不在意最后的矩阵到底是什么样子的,我们如果能够明白构建矩阵的规律,那么就可以直接按照规律去取数,不需要真正的构建矩阵,那么我们先研究下当行数numRows 小于4的时候分别是什么样子。
numRows 为1:
0 1 2 3 4 5 6 7 ... n
numRows 为2:
0 2 4 6 ... n-1 1 3 5 7 ... n
numRows 为3
0 4 8 12 1 3 5 7 9 11 2 6 10
numRows 为4
0 6 12 18 1 5 7 11 13 17 2 4 8 10 14 16 3 9 15
可以看到其实所有的变幻操作都是在循环的打钩 [ √ ],以numRows=4为例:
0 | 6 | 12 |18 1 5 | 7 11 | 13 17 |19 23 2 4 | 8 10 | 14 16 |20 22 3 | 9 | 15 |21
而每次打钩 [ √ ]所需要的字符个数t也是和numRows有关:
当numRows=1时:t=1 当numRows=2时:t=2 当numRows=3时:t=4 当numRows=4时:t=6 ... 当numRows=n时:t=2*n-2
那这样我们就可以推断出numRows=n的时候,所循环的钩 [ √ ]应该如下[t=2*n-2]:
0 1 t-1 2 t-2 3 t-3 4 t-4 5 t-5 6 t-6 ...... n-1
那这个时候我们只需要用for循环按行去读取对应的字符,也就不需要在代码中构建出最后的矩阵了。
AC代码
模拟
点击查看代码
class Solution {
public String convert(String s, int numRows) {
if( numRows == 1 ) {
return s;
}
char[] chars = s.toCharArray();
int len = chars.length;
char[][] charss = new char[numRows][len];
int i = 0;
int j = 0;
int index = 0;
while( index<len ) {
if( i==0 ) {
if( j!=0 ) {
// 第一次的时候是以第一行为起点,其他循环次数的时候是一第二行为起点
i ++;
}
while( i < numRows && index<len ) {
charss[i][j] = chars[index];
i ++;
index ++;
}
i --;
}
if( i == numRows-1 ) {
while(i>0 && index<len ) {
i --;
j ++;
charss[i][j] = chars[index];
index ++;
}
}
}
StringBuffer sb = new StringBuffer();
for(i=0; i<numRows; i++) {
System.out.println();
for(j=0; j<len; j++) {
System.out.print(charss[i][j] + " ");
if(Character.isLetter(charss[i][j]) || charss[i][j]=='.' || charss[i][j]==',') {
sb.append(charss[i][j]);
}
}
}
return sb.toString();
}
}
直接构造
点击查看代码
class Solution {
public String convert(String s, int numRows) {
int len = s.length();
if (numRows == 1 || numRows >= len) {
return s;
}
char[] chars = s.toCharArray();
StringBuffer sb = new StringBuffer();
int t = numRows * 2 - 2;
for (int i = 0; i < numRows; ++i) {
// 循环打√
for (int j = 0; j + i < len; j += t) {
sb.append(chars[j+i]);
// 找一次循环的第二个值,同时第一行和最后一行只有一个值,不需要找
if (0 < i && i < numRows - 1 && j + t - i < len) {
sb.append(chars[j + t - i]);
}
}
}
return sb.toString();
}
}