2019.1.23
题目描述:
The string "PAYPALISHIRING"
is written in a zigzag pattern on a given number of rows like this: (you may want to display this pattern in a fixed font for better legibility)
P A H N A P L S I I G Y I R
And then read line by line: "PAHNAPLSIIGYIR"
Write the code that will take a string and make this conversion given a number of rows:
string convert(string s, int numRows);
Example 1:
Input: s = "PAYPALISHIRING", numRows = 3 Output: "PAHNAPLSIIGYIR"
Example 2:
Input: s = "PAYPALISHIRING", numRows = 4 Output: "PINALSIGYAHRPI" Explanation: P I N A L S I G Y A H R P I
这题我题目是看懂了,但是好半天也做不出来,只是隐约觉得应该是找规律的题目,就是找不出规律Orz。。万般无奈之下只好直接看了官方题解和讨论和相关的博客。
解法一:官方解法
恍然大悟,也就是说,由于最后输出的是不包含空格的字符串,所以我们其实并不需要管每一行中间在哪里会出现空格,而只需要将每个字符应该处在Z字形图案的哪一行确定下来就可以了,这里采用的方法是每当我们新字符串的指针移动到第一行的时候,就开始从上至下填充字符(也即行数递增),而到最后一行的时候,我们需要改变指针的方向,开始从下到上填充字符(也即行数递减)。
反思:我思考的时候一直在考虑新字符串组每一行具体应该存储什么,将问题复杂化了,其实每一行出现的空格对结果没有一点影响,因为最终输出的是不包含空格的字符串,只需要将每个字符应该处在Z字形图案的哪一行确定下来就可以了,所以Z字形图案也完全可以理解为输出字符的方式是从上至下变为从下至上再变为从上至下的循环过程即可。
C++代码:
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;
}
};
解法二:官方解法
这个方法就纯粹是找数学规律了。。也就是说我们直接按输出时的每一行来依次访问,同时求出每一行元素的index的算法。
求index的算法都在上面,就不多说了。
C++代码:
class Solution {
public:
string convert(string s, int numRows) {
if (numRows == 1) return s;
string ret;
int n = s.size();
int cycleLen = 2 * numRows - 2;
for (int i = 0; i < numRows; i++) {
for (int j = 0; j + i < n; j += cycleLen) {
ret += s[j + i];
if (i != 0 && i != numRows - 1 && j + cycleLen - i < n)
ret += s[j + cycleLen - i];
}
}
return ret;
}
};
解法三:
其实和解法二大同小异。
这里我们参考该博主的博客,他的博客更清楚。
http://www.cnblogs.com/springfor/p/3889414.html,
可能有懒癌晚期的同学,我就把这博客的重点贴出来吧:
我们来举个例子:
n=2时,字符串坐标变成zigzag的走法就是:
0 2 4 6
1 3 5 7
n=3时的走法是:
0 4 8
1 3 5 7 9
2 6 10
n=4时的走法是:
0 6 12
1 5 7 11 13
2 4 8 10 14
3 9 15
可以发现规律,画红色的长度永远是 2n-2 (想法是你试想把所有这些行压缩成两列,两边手挤一下,第二列永远的第一行和最后一行少字)。
利用这个规律,可以按行填字,第一行和最后一行,就是按照2n-2的顺序一点点加的。
其他行除了上面那个填字规则,就是还要处理斜着那条线的字,可以发现那条线的字的位置永远是当前列j+(2n-2)-2i(i是行的index)。
也就是说,首尾两行的元素的index是和numRows相关的,即2*numRows-2,而其他行去掉斜线部分的元素,其实也是按照这个规则排列的。接下来我们处理斜线部分,斜线部分红色元素的index其实也是有规律的,为 j + 2*nRows-2 - 2*i, 其中,j为前一个黑色元素的列数,i为当前行数,知道了所有元素的index的算法,代码也就出来了。
C++代码:
class Solution {
public:
string convert(string s, int numRows) {
if (numRows <= 1) return s;
string res = "";
int size = 2 * numRows - 2;
for (int i = 0; i < numRows; ++i) {
for (int j = i; j < s.size(); j += size) {
res += s[j];
int tmp = j + size - 2 * i;
if (i != 0 && i != numRows - 1 && tmp < s.size()) res += s[tmp];
}
}
return res;
}
};
PS.我觉得这题也就解法一有点价值,让我知道了考虑问题应该深入本质,要搞清楚最终的目标是什么,其他的解法就是纯粹的数学题了,个人感觉没必要深究了。