leetcode算法题6——Z字形变换

Z字形变换(难度:中等)

将一个给定字符串根据给定的行数,以从上往下、从左到右进行 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」的方式排列字符,就是下边的样子:
leetcode算法题6——Z字形变换_第1张图片
然后按行的方式输出每个字符,第 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 形排列后字符的规律,然后直接保存起来
leetcode算法题6——Z字形变换_第2张图片
我们可以看到,图形其实是有周期的,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;
    }
};

你可能感兴趣的:(LeetCode,字符串,数据结构,算法,leetcode)