根据题目的意思,最初的想法是模拟,用一个二维数组模拟字符串在其中的排列,再用一个字符串按顺序接收二维数组中字符串,以此来得到答案。
方法一:具体的实现为首先判断字符串的长度,排除特殊情况。再用排列二维数组的周期t(t = r + r - 2 = 2 * r - 2,即先向二维数组的下方写入r个字符,再向右上方写入r - 2个字符),算出一个用多少列(用n / t可得要有多少个周期,再乘以(r - 1)行,可得矩阵的列数,这里用(n + t - 1/ t)是为了求余 的时候可以向上取整。)具体的实现代码如下:
class Solution {
public:
string convert(string s, int numRows) {
int n = s.length(),r = numRows;
if(r == 1 || r >= n) return s;//初始条件判断
int t = 2 * r - 2;//记录字符排序的周期
int c = (n + t - 1) / t * (r - 1);//字符排序的列数
vector<string> mat(r,string(c,0));
for(int i = 0,x = 0,y = 0;i < n;i++){
mat[x][y] = s[i];//将字符串赋值给二维数组
if(i % t < (r - 1){//将字符向下赋值
++x;
}else//{将字符向右上赋值
--x;
++y;
}
}
string ans;
for(auto &rt : mat){
for(char row : rt){
if(ch){
ans += ch;//将二维数组中的值赋值给字符串
}
}
}
return ans;
}
};
二维数组模拟所消耗的时间和空间较大,可以进行进一步的优化。
方法二:对于知道最后位置和初始位置关系,可以直接构造出初始位置和最后关系的函数,然后可以直接得到最终结果。
前面几步和方法一一样,后面的在于不用把字符串在二维数组中模拟,而是直接跳着连接上所有字符。第一层循环是行的递增,逐步增加。第二层循环是对于列来说,列的增加是以周期t为准,以正Z字形方式累加得到最后的结果。
class Solution {
public:
string convert(string s, int numRows) {
int n = s.length(),r = numRows;
if(r == 1 || r >= n){
return s;
}
string ans;
int t = 2 * r - 2;
for(int i = 0;i < r;i++){
for(int j = 0;j + i < n;j += t){
ans += s[j + i];
if(i > 0 && i < r - 1 && j + t - i < n){//右上角的字符
ans += s[j + t - i];
}
}
}
return ans;
}
};
该方法的时间和空间效率都有很大的提高,而且较为容易理解,很适合学习。