将一个给定字符串根据给定的行数,以从上往下、从左到右进行 Z 字形排列。
比如输入字符串为 “LEETCODEISHIRING” 行数为 3 时,排列如下:
L C I R
E T O E S I I G
E D H N
之后,你的输出需要从左往右逐行读取,产生出一个新的字符串,比如:“LCIRETOESIIGEDHN”。
请你实现这个将字符串进行指定行数变换的函数:
string convert(string s, int numRows);
示例 1:
输入: s = "LEETCODEISHIRING", numRows = 3
输出: "LCIRETOESIIGEDHN"
示例 2:
输入: s = "LEETCODEISHIRING", numRows = 4
输出: "LDREOEIIECIHNTSG"
解释:
L D R
E O E I I
E C I H N
T S G
答:不晓得出题人咋想的,明明是反N形变化,非要说成Z字形的,可能外国人的脑回路和咱们不一样吧。。
这个题目的意思是 给定一个字符串,然后按写竖着的 「z」的方式排列字符,就是下边的样子:
然后按行的方式输出每个字符,第 0 行,第 1 行,第 2 行 …即可
比如有一个字符串 “0123456789ABCDEF”
当 n = 2 时:
0 2 4 6 8 A C E
1 3 5 7 9 B D F
当 n = 3 时:
0 4 8 C
1 3 5 7 9 B D F
2 6 A E
当 n = 4 时:
0 6 C
1 5 7 B D
2 4 8 A E
3 9 F
方法一:按行排序
我们可以使用min(numRows,len(s)) 个列表来表示 Z 字形图案中的非空行。
从左到右迭代 ss,将每个字符添加到合适的行。可以使用当前行和当前方向这两个变量对合适的行进行跟踪。
只有当我们向上移动到最上面的行或向下移动到最下面的行时,当前方向才会发生改变。
代码如下:
class Solution {
public:
string convert(string s, int numRows) {
if (numRows == 1) return s;
vector<string> rows(min(numRows, int(s.size())));
int curRow = 0;
bool goingDown = false;
for (char c : s) {
rows[curRow] += c;
if (curRow == 0 || curRow == numRows - 1) goingDown = !goingDown;
curRow += goingDown ? 1 : -1;
}
string ret;
for (string row : rows) ret += row;
return ret;
}
};
这个方法太厉害了…定义了一个布尔型变量goingDown,然后遍历每一个字符s,存入到rows[]中去,只有到了第一行或最后一行时,才让goingDown方向改变,即本来是curRow++,就变成curRow–,这样能保证curRow的范围始终在[0-numRows-1]中,而且s[i]始终是原字符串对应的字符,这样就把字符串s存入到了Z字形变换的rows[]字符串数组中。
思路相同,另一种写法如下:
建立一个大小为 numRows 的字符串数组,为的就是把之字形的数组整个存进去,然后再把每一行的字符拼接起来,就是想要的结果了。顺序就是按列进行遍历,首先前 numRows 个字符就是按顺序存在每行的第一个位置,然后就是 ‘之’ 字形的连接位置了,可以发现其实都是在行数区间 [1, numRows-2] 内,只要按顺序去取字符就可以了,最后把每行都拼接起来即为所求,参见代码如下:
class Solution {
public:
string convert(string s, int numRows) {
if (numRows <= 1) return s;
string res;
int i = 0, n = s.size();
vector<string> vec(numRows);
while (i < n) {
for (int pos = 0; pos < numRows && i < n; ++pos) {
vec[pos] += s[i++];
}
for (int pos = numRows - 2; pos >= 1 && i < n; --pos) {
vec[pos] += s[i++];
}
}
for (auto &a : vec) res += a;
return res;
}
};
方法二:找出按 Z 形排列后字符的规律,然后直接保存起来
我们可以看到,图形其实是有周期的,0,1,2 … 7 总过 8 个,然后就又开始重复相同的路径。周期的计算就是 size = 2 × numRows - 2 = 2 × 5 - 2 = 8 个。
我们发现第 0 行和最后一行一个周期内有一个字符,所以第一个字符下标是 0 ,第二个字符下标是 0 + size = 8,第三个字符下标是 8 + size = 16 。
其他行都是两个字符。
第 1 个字符和第 0 行的规律是一样的。
第 2 个字符是 j + size - 2 x i,比如5=3+8-23,6=2+8-4,13=11+8-23。。。
代码如下:
class Solution {
public:
string convert(string s, int numRows) {
if (numRows <= 1) return s;
string res;
int size = 2 * numRows - 2, n = s.size();
for (int i = 0; i < numRows; ++i) {
for (int j = i; j < n; j += size) {
res += s[j];
int pos = j + size - 2 * i;
if (i != 0 && i != numRows - 1 && pos < n) res += s[pos];
}
}
return res;
}
};