模拟题
题目描述
将一个给定字符串 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
来源:
力扣(LeetCode)
思路
这道模拟题的关键思路是坐标变换,即需要对两个字符串的坐标进行映射。
假定输入字符串为s
,输出字符串为p
,那么映射的思路分两种,一种是从第一个下标开始遍历s
,寻找在p
对应的下标;第二种思路是反过来从第一个下标遍历p
,寻找在字符串s
中对应的下标。第二种思路会更好计算一点。
观察字符串s
的“前进”规律可以发现,字符串中的字符要么是向下运动,要么是斜线向上运动。
而对于字符串p
,它是负责逐字符行打印Z
型中的每一行的。
计算规律如下:
- 对于
z
型的任意第i
行的起始字符,都对应字符串s
的第i
个字符。 - 对于
z
型的任意字符来说,只要该在z
字型中往右移动一次,要么是通过先向下移动再斜向上移动的方式,要么是通过先斜向上移动再向下移动的方式进行。 - 对于首行
z
型字符而言,始终往右移一位是通过向下移动再斜向上移动方式进行。 - 对于末行
z
型字符而言,始终往右移一位是通过向下移动再斜向上移动方式进行。 - 对于既非首行也非末行的其它
z
型字符而言,从首列开始,往右移动的移动规律是先向下移动再斜向上移动,下一步骤是先斜向上再向下移动交替进行。
利用如上描述的规律,就可以写代码模拟z
型运动逻辑。通过逐行扫描z
型字符,即可把对应的字符写入到p
中。
swift
代码:
class Solution {
func convert(_ s: String, _ numRows: Int) -> String {
guard numRows > 1 else { return s }
let cs = s.cString(using: .ascii)!
let count = cs.count
var ans: [CChar] = Array(repeating: 0, count: count)
var i1 = 0
var i0 = 0
var row = 0
// 标记下一步移动是先向下,还是先斜向上
var down = true
while i1 < count - 1 {
ans[i1] = cs[i0]
i1 += 1
// 对于不同的移动方式,计算对应的偏移索引
i0 += down ? (numRows-row-1)*2 : row*2
if i0 >= count - 1 {
row += 1
i0 = row
down = row == numRows - 1 ? false : true
} else if row != 0 && row != numRows - 1 {
down.toggle()
}
}
return String(cString: ans)
}
}