1. 首页
  2. LeetCode

每日一道 LeetCode (49):Z 字形变换

每日一道 LeetCode (49):Z 字形变换

每天 3 分钟,走上算法的逆袭之路。

前文合集

每日一道 LeetCode 前文合集

代码仓库

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<StringBuilder> 的集合,也算是达到了一个二维数组的概念。

public String convert(String s, int numRows) {
    // 极限值判断
    if (numRows == 1) return s;
    // 定义行
    List<StringBuilder> 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();
}

这个答案只能说仅供参考吧,完全不知道是怎么推导出来的。这道题主要是要记住第一个解题方案中的逆向迭代的方案,可以加一个转向标记位。

转载声明:本博客由极客挖掘机创作,采用 CC BY 3.0 CN 许可协议。可自由转载、引用,但需署名作者且注明文章出处。如转载至微信公众号,请在文末添加作者公众号二维码。

发表评论

电子邮件地址不会被公开。 必填项已用*标注

QR code