6. Z 字形变换
将一个给定字符串 s
根据给定的行数 numRows
,以从上往下、从左到右进行 Z 字形排列。
示例:
输入:s = "PAYPALISHIRING", numRows = 4
输出:"PINALSIGYAHRPI"
解释:
P I N
A L S I G
Y A H R
P I
思路:
根据题意,当我们在矩阵上填写字符时,会向下填写 r 个字符,然后向右上继续填写 r−2 个字符,最后回到第一行,因此 Z 字形变换的周期 t=r+r−2=2r−2,每个周期会占用矩阵上的 1+r-2=r-1 列。
创建一个 r 行 c 列的矩阵,然后遍历字符串 s 并按 Z 字形填写。具体来说,设当前填写的位置为 (x,y),即矩阵的 x 行 y 列。初始 (x,y)=(0,0),即矩阵左上角。若当前字符下标 i 满足 i%t<r−1,则向下移动,否则向右上移动。
class Solution {
public String convert(String s, int numRows) {
int n = s.length(), r = numRows;
if (r == 1 || r >= n) {
return s;
}
int t = r * 2 - 2; // Z字形变换的周期 t=r+r-2=2r-2
int c = (n + t - 1) / t * (r - 1); //周期(向上取整)*列数
char[][] mat = new char[r][c];
for (int i = 0, x = 0, y = 0; i < n; ++i) {
mat[x][y] = s.charAt(i);
if (i % t < r - 1) {
++x; // 向下移动
} else {
--x;
++y; // 向右上移动
}
}
StringBuffer ans = new StringBuffer();
for (char[] row : mat) {
for (char ch : row) {
if (ch != 0) {
ans.append(ch);
}
}
}
return ans.toString();
}
}
复杂度分析
- 时间复杂度:O(r⋅n),其中r=numRows,n为字符串 s 的长度。时间主要消耗在矩阵的创建和遍历上,矩阵的行数为 r,列数可以视为 O(n)。
- 空间复杂度:O(r⋅n)。矩阵需要O(r⋅n) 的空间。
改进:压缩矩阵空间
思路:
注意到每次往矩阵的某一行添加字符时,都会添加到该行上一个字符的右侧,且最后组成答案时只会用到每行的非空字符。因此我们可以将矩阵的每行初始化为一个空列表,每次向某一行添加字符时,添加到该行的列表末尾即可。
class Solution {
public String convert(String s, int numRows) {
int n = s.length(), r = numRows;
if (r == 1 || r >= n) {
return s;
}
StringBuffer[] mat = new StringBuffer[r];
for (int i = 0; i < r; ++i) {
mat[i] = new StringBuffer();
}
for (int i = 0, x = 0, t = r * 2 - 2; i < n; ++i) {
mat[x].append(s.charAt(i));//添加到当前列表的末尾
if (i % t < r - 1) {
++x;
} else {
--x;
}
}
StringBuffer ans = new StringBuffer();
for (StringBuffer row : mat) {
ans.append(row);
}
return ans.toString();
}
}
复杂度分析
-
时间复杂度:O(n)。
-
空间复杂度:O(n)。压缩后的矩阵需要 O(n)的空间。