题目难度:中等
默认优化目标:最小化平均时间复杂度。
Python默认为Python3。
目录
1 题目描述
将一个给定字符串 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
2 题目解析
输入是一个字符串s
和行数numRows
,输出是Z字形变换后的字符串。假设s=PAYPALISHIRING
,numsRows=3
,则Z字形变换结果如下:
P | A | H | N | |||
---|---|---|---|---|---|---|
A | P | L | S | I | I | G |
Y | I | R |
如果s=PAYPALISHIRING
,numsRows=4
,则Z字形变换结果如下:
P | I | N | ||||
---|---|---|---|---|---|---|
A | L | S | I | G | ||
Y | A | H | R | |||
P | I |
3 算法原理及代码实现
3.1 模拟
我们可以创建一个二维数组来模拟这个过程。通过观察我们可以发现,当numRows=1
或者numRows>=n
,Z变换后的字符串Zs
与s
相同。除此之外,向矩阵填写字符时,从左上向左下填写numRows
个字符,然后斜向上45度填写numRows-2
个字符,我们把这一次操作作为一个周期t=numRows+numRows-2=2numRows-2
,涉及到numRows-1
列。对于s
来说,总共有个周期,n
为s
的长度。因此,二维矩阵的列数为。
接下来,我们按照上述方法填充这个二维矩阵,最后在按行从左往右将非空元素读取出来即可。
平均时间复杂度为O(numRows·n),平均空间复杂度为O(numRows·n)。
C++代码实现
class Solution {
public:
string convert(string s, int numRows) {
int n=s.size();
if(numRows!=1 && numRows<n){
int t=numRows*2-2;
int c=(n+t-1)/t*(numRows-1);
vector<string> mat(numRows,string(c,0));
for(int i=0,x=0,y=0;i<n;i++){
mat[x][y]=s[i];
if(i%t<numRows-1){
x++;
}
else{
x--;
y++;
}
}
string Zs;
for(auto &row : mat){
for(char ch : row){
if(ch){
Zs+=ch;
}
}
}
return Zs;
}
else{
return s;
}
}
};
Python代码实现
class Solution:
def convert(self, s: str, numRows: int) -> str:
n = len(s)
if numRows != 1 and numRows < n:
t = numRows * 2 - 2
c = (n + t - 1) // t * (numRows - 1)
mat = [['' for _ in range(c)] for _ in range(numRows)]
x, y = 0, 0
for i in range(n):
mat[x][y] = s[i]
if i % t < numRows - 1:
x += 1
else:
x -= 1
y += 1
Zs = ''.join([''.join(row) for row in mat if row])
return Zs
else:
return s
Java代码实现
class Solution {
public String convert(String s, int numRows) {
int n = s.length();
if (numRows != 1 && numRows < n) {
int t = numRows * 2 - 2;
int c = (n + t - 1) / t * (numRows - 1);
char[][] mat = new char[numRows][c];
int x = 0, y = 0;
for (int i = 0; i < n; i++) {
mat[x][y] = s.charAt(i);
if (i % t < numRows - 1) {
x++;
} else {
x--;
y++;
}
}
StringBuilder Zs = new StringBuilder();
for (char[] row : mat) {
for (char ch : row) {
if (ch != 0) {
Zs.append(ch);
}
}
}
return Zs.toString();
} else {
return s;
}
}
}
进一步优化,我们能不能把“空”的存储空间去掉呢?我们可以借用链表的思想。我们将矩阵的每一行初始化成一个列表,每次从表尾插入元素。
平均时间复杂度为O(n),平均空间复杂度为O(n)。
C++代码实现
class Solution {
public:
string convert(string s, int numRows) {
int n=s.size();
if(numRows!=1 && numRows<n){
vector<string> mat(numRows);
for(int i=0,x=0,t=numRows*2-2;i<n;i++){
mat[x]+=s[i];
i%t<numRows-1?x++:x--;
}
string Zs;
for(auto &row : mat){
Zs+=row;
}
return Zs;
}
else{
return s;
}
}
};
Python代码实现
class Solution:
def convert(self, s: str, numRows: int) -> str:
n = len(s)
if numRows != 1 and numRows < n:
mat = ['' for _ in range(numRows)]
x, t = 0, numRows * 2 - 2
for i in range(n):
mat[x] += s[i]
if i % t < numRows - 1:
x += 1
else:
x -= 1
Zs = ''.join(mat)
return Zs
else:
return s
Java代码实现
class Solution {
public String convert(String s, int numRows) {
int n = s.length();
if (numRows != 1 && numRows < n) {
String[] mat = new String[numRows];
for (int i = 0; i < numRows; i++) {
mat[i] = "";
}
int x = 0, t = numRows * 2 - 2;
for (int i = 0; i < n; i++) {
mat[x] += s.charAt(i);
if (i % t < numRows - 1) {
x++;
} else {
x--;
}
}
StringBuilder Zs = new StringBuilder();
for (String row : mat) {
Zs.append(row);
}
return Zs.toString();
} else {
return s;
}
}
}
3.2 直接构造
Z字形变换周期t=2·numRows-2
,对于第一行,Zs
下标idx=0 (mod t)
。对于最后一行,idx=r-1 (mod t)
。对于其余行号,第一个字符idx=i (mod t)
,第二个字符idx=t-i (mod t)
。
平均时间复杂度O(n),平均空间复杂度O(1)。
C++代码实现
class Solution {
public:
string convert(string s, int numRows) {
int n=s.size();
if(numRows!=1 && numRows<n){
string Zs;
int t=numRows*2-2;
for(int i=0;i<numRows;i++){
for(int j=0;j+i<n;j+=t){
Zs+=s[j+i];
if(0<i && i<numRows-1 && j+t-i<n){
Zs+=s[j+t-i];
}
}
}
return Zs;
}
else{
return s;
}
}
};
Python代码实现
class Solution:
def convert(self, s: str, numRows: int) -> str:
n = len(s)
if numRows != 1 and numRows < n:
Zs = ""
t = numRows * 2 - 2
for i in range(numRows):
for j in range(0, n - i, t):
Zs += s[j + i]
if 0 < i < numRows - 1 and j + t - i < n:
Zs += s[j + t - i]
return Zs
else:
return s
Java代码实现
class Solution {
public String convert(String s, int numRows) {
int n = s.length();
if (numRows != 1 && numRows < n) {
StringBuilder Zs = new StringBuilder();
int t = numRows * 2 - 2;
for (int i = 0; i < numRows; i++) {
for (int j = 0; j + i < n; j += t) {
Zs.append(s.charAt(j + i));
if (i > 0 && i < numRows - 1 && j + t - i < n) {
Zs.append(s.charAt(j + t - i));
}
}
}
return Zs.toString();
} else {
return s;
}
}
}