Offer 驾到,掘友接招!我正在参与2022春招打卡活动,点击查看活动详情。
前言
今日的题目为中等,最简单的方法就是用一个二维数组去模拟平面,知道了移动的过程之后,用数组来进行模拟就可以很容易的解题
每日一题
今天的每日一题 6. Z 字形变换,难度为中等
-
将一个给定字符串 s 根据给定的行数 numRows ,以从上往下、从左到右进行 Z 字形排列。
-
比如输入字符串为 "PAYPALISHIRING" 行数为 3 时,排列如下:
-
P - A - H - N
-
A P L S I I G
-
Y - I - R
-
之后,你的输出需要从左往右逐行读取,产生出一个新的字符串,比如:"PAHNAPLSIIGYIR"。
-
请你实现这个将字符串进行指定行数变换的函数:
-
string convert(string s, int numRows);
示例 1:
输入:s = "PAYPALISHIRING", numRows = 3
输出:"PAHNAPLSIIGYIR"
示例 2:
输入:s = "PAYPALISHIRING", numRows = 4
输出:"PINALSIGYAHRPI"
解释:
P I N
A L S I G
Y A H R
P I
示例 3:
输入:s = "A", numRows = 1
输出:"A"
提示:
- 1 <= s.length <= 1000
- s 由英文字母(小写和大写)、',' 和 '.' 组成
- 1 <= numRows <= 1000
题解
利用二维数组直接模拟
分析移动过程
题目需要我们将一个字符串按照 ‘z’ 字排列,所以我们可以用一个二维数组来模拟平面,直接在上面进行平移,通过题目给的第一个例子和第二个例子,我们可以来分析一下应该要怎么移动:
由两个例子应该能看出我们就是按照一个 ‘z’ 字来进行移动,一开始的时候会向下移动,在移动 numRows-1 位的时候换为向右上移动,在移动 numRows-1 之后又换位向下移动,一直重复,知道遍历完全部的字符串。
创建模拟过程的二维数组
那么再有了上面的基础上,我们就只需要去创建两个空数组,数组的长度和宽度偷懒的话可以直接生成和字符串长度相同的,就一定能满足要求。
不过写题就要考虑一下内存问题,从上面的图可以看出来,我们生成的二维数组的大小,数组的高就一定是和 numRows 一样,数组的宽就可以通过计算得出。
由上面的移动过程,我们可以看出来其实移动是重复的,每一次重复次数为 re,向下移动 numRows-1 在 向右上移动 numRows-1 为一个循环,所以我们能得到一次循环 re 为 numRows*2 -2。并且每一次循环会占用的宽度为 r−1。
然后知道了总的字符串长度,又知道了每一次需要的宽度和使用的字符串的长度,我们就能够得出一个趋近于最小需要的宽度:
const w = Math.floor((s.length + re - 1) / re) * (numRows - 1);
利用二维数组模拟过程
有了数组之后,我们初始化点的位置在 (0,0) 从这里开始模拟移动的过程,并且把移动过的位置更新值为当前遍历到的字符。
模拟完成之后,再次便利字符串并且把有值得位置拼接起来,就是题目需要的答案。
代码
/**
* @param {string} s
* @param {number} numRows
* @return {string}
*/
var convert = function(s, numRows) {
if (numRows === 1 || numRows >= s.length) {
return s;
}
const re = numRows * 2 - 2;
const w = Math.floor((s.length + re - 1) / re) * (numRows - 1);
const arr = new Array(numRows).fill(0).map(() => new Array(w).fill(0));
for (let i = 0, x = 0, y = 0; i < s.length; i++) {
arr[x][y] = s[i];
if (i % re < numRows - 1) {
x++;
} else {
x--;
y++;
}
}
const ans = [];
for (const row of arr) {
for (const ch of row) {
if (ch !== 0) {
ans.push(ch);
}
}
}
return ans.join('');
};