每日一道 LeetCode (49):Z 字形变换
❝每天 3 分钟,走上算法的逆袭之路。
❞
前文合集
代码仓库
GitHub:https://github.com/meteor1993/LeetCode
Gitee:https://gitee.com/inwsy/LeetCode
题目:最长回文子串
难度:「中等」
题目来源:https://leetcode-cn.com/problems/zigzag-conversion/
将一个给定字符串根据给定的行数,以从上往下、从左到右进行 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
解题方案一:按行排序
这种题就是那种点型的一看不会做,一看答案感觉自己是怕不是个智障。
最开始的时候想着使用纯暴力的方案去排那个字符串,但是这个字符串由于是 Z 字型的,那就需要用到二维数组,愣是没想通先竖着排完以后如何转向的问题。
不得已之下去看了答案,看完以后恨不得抽自己俩大嘴巴子,直接用正负 1 来控制输出的转向就这么难想么?
哎。。。感觉别人的脑子是个脑子,自己的脑子可能它只是挂了个名字。。。
答案中并没有选择使用数组,而是使用了一个 List
的集合,也算是达到了一个二维数组的概念。
public String convert(String s, int numRows) {
// 极限值判断
if (numRows == 1) return s;
// 定义行
List rows = new ArrayList<>();
// 每一行都增加一个列
for (int i = 0; i < Math.min(numRows, s.length()); i++)
rows.add(new StringBuilder());
// 定义当前行数
int curRow = 0;
// 定义是否转向
boolean goingDown = false;
for (char c : s.toCharArray()) {
// 当前行添加一个字符
rows.get(curRow).append(c);
// 如果当前行是第一行或者是最后一行,转向标记位取反
if (curRow == 0 || curRow == numRows - 1) goingDown = !goingDown;
// 注意:这里计算了下一次循环的行数,并且完成了循环中转向
curRow += goingDown ? 1 : -1;
}
// 定义输出
StringBuilder ret = new StringBuilder();
for (StringBuilder row : rows) ret.append(row);
return ret.toString();
}
代码我就不解释了,每一行的注释都加好了,这个循环中转向的思路我以后绝对会记住的,简直是奇耻大辱。
解题方案二:按行访问
这个方案绝对是 NB 的大神拿个纸和笔坐在那活活推导出来的结果,就一个字,服!
这个方案属于那种直接写结果的方案,直接按照逐行读取读取的顺序来输出整个字符串。
这我还能说啥,知道了公式以后不就是照着公式写代码嘛,不知道公式就只能干瞪眼。
其实我在刚看到这道题的时候就想,这个通项公式肯定存在,以我的能力估计是推导不出来,这个方案直接 pass 掉了。
果然在答案中是能人辈出,直接给出通项公式:
对于所有整数 k 行 0 中的字符位于索引 k (2 * numRows - 2) 处。 行 numRows - 1 中的字符位于索引 k (2 * numRows - 2) + numRows - 1 处。 内部的行 i 中的字符位于索引 k (2 * numRows - 2) + i 以及 (k + 1)(2 * numRows - 2) - i 处。
public String convert_1(String s, int numRows) {
if (numRows == 1) return s;
// 定义输出
StringBuilder ret = new StringBuilder();
int n = s.length();
int cycleLen = 2 * numRows - 2;
for (int i = 0; i < numRows; i++) {
for (int j = 0; j + i < n; j += cycleLen) {
ret.append(s.charAt(j + i));
if (i != 0 && i != numRows - 1 && j + cycleLen - i < n)
ret.append(s.charAt(j + cycleLen - i));
}
}
return ret.toString();
}
这个答案只能说仅供参考吧,完全不知道是怎么推导出来的。这道题主要是要记住第一个解题方案中的逆向迭代的方案,可以加一个转向标记位。