Z 字形变换
今天的题目是力扣面试经典150题中的数组的中等难度题: Z 字形变换。
题目链接:https://leetcode.cn/problems/zigzag-conversion/description/?envType=study-plan-v2&envId=top-interview-150
题目描述
将一个给定字符串 s 根据给定的行数 numRows ,以从上往下、从左到右进行 Z 字形排列。
比如输入字符串为 “PAYPALISHIRING” 行数为 3 时,排列如下:
P A H N
A P L S I I G
Y I R
之后,你的输出需要从左往右逐行读取,产生出一个新的字符串,比如:“PAHNAPLSIIGYIR”。
-
示例 1:
-
输入:
s = “PAYPALISHIRING”, numRows = 3 -
输出:
“PAHNAPLSIIGYIR” -
解释:
P A H N A P L S I I G Y I R
-
-
示例 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” -
解释:
没法转换,直接返回字符串
-
题目分析
题目会给定一个字符串和一个数字num,要求把字符串分成num行。在分行的时候,要求按列Z字形排列。
其实这个Z字形可能不是很直观,再拆分成一个类似以
字的左边的竖提就清晰一点。
用示例来讲解一下:
- 示例一:
P-A-Y-P组成第一个竖提,A-L-I-S组成第二个竖提,H-I-R-I组成第三个竖提,最后的N-G组成一个不完整的竖提。 - 示例二:
P-A-Y-P-A-L组成第一个竖提,I-S-H-I-R-I组成第二个竖提,N-G组成最后一个不完成的竖提。
仔细观察,是不是知道了怎么按行数排列字符串了。
排列好字符串以后,我们从左到右,从第一排到最后一排,把数组全部拼接起来即可。
解题思路
目前来看,以作者的水平这个题目没有能够方便使用的算法,只能暴力破解。
- 首先确认拼接方式,我们按行数循环,凭借每行的字符,因为题目要求我们从左往右拼接给出结果。
- 确认每行中每列字符在字符串中的索引。根据前面的分析,三行的时候四个字符组成一个竖提,也就是说,每次循环需要四个字符,四行的时候六个字符组成一个竖提,我们自己再尝试两行,五行等情况.
最后你会发现每次循环的字符数 = 2*(num - 1)。 2 = 2*(2-1),4 = 2*(3-1),6 = 2*(4-1),8 = 2*(5-1)。。。。。 - 那么我们可以创建num个数组,每个数组 存放一行的内容。
- 循环字符串,使用当前下标对当前行数对应的字符数取模,取模的值与行数比较,如果小于行数的,直接使用当前取模值为当前行数。否则使用字符数减去取模值。注意因为我们循环是从0开始,所以这里的实际行数应该加1。比如取模值为0,实际是第一行。
- 拼接所有行的字符。
根据以上分析,我们可以编写实现代码。
实际算法代码
以下是暴力解答的代码实现:
public class ZigzagConversion {
public static void main(String[] args) {
ZigzagConversion solution = new ZigzagConversion();
System.out.println(solution.convert("PAYPALISHIRING", 3)); // 输出 "PAHNAPLSIIGYIR"
System.out.println(solution.convert("PAYPALISHIRING", 4)); // 输出 "PINALSIGYAHRPI"
}
public String convert(String s, int numRows) {
// 行数是1的矩阵或者行数就等于字符串长度的,直接返回字符串本身
if (numRows == 1 || numRows >= s.length()) return s;
// 定义一个数组,用来存储每一行的字符串
StringBuilder[] rows = new StringBuilder[numRows];
// 定义每行的拼接对象,注意行数 = i-1
for (int i = 0; i < numRows; i++) {
rows[i] = new StringBuilder();
}
// 获取字符串长度用于循环
int n = s.length();
// 获取每次循环时构成Z性的字符长度
int cycleLen = 2 * numRows - 2;
// 循环字符串
for (int i = 0; i < n; i++) {
// 取模
int rowIndex = i % cycleLen;
// 小于行数的索引,行数就等于索引,大于的用字符数减取模值,因为此时从下往上排列
if (rowIndex < numRows) {
rows[rowIndex].append(s.charAt(i));
} else {
rows[cycleLen - rowIndex].append(s.charAt(i));
}
}
// 拼接结果
StringBuilder result = new StringBuilder();
for (StringBuilder row : rows) {
result.append(row);
}
return result.toString();
}
}
结果
运行程序,正常输出答案:
提交到力扣,也通过测试:
总结
又是暴力解答的一题,没有已经归纳好的算法,单纯就是总结规律加上字符串数组循环拼接的基础知识。
善于分析归纳,扎实的基础,基本上没有解不出的题目,暴力解答也是解答,别拿豆包不当干粮,是吧?
准备直面困难题目了,加油!!!