LeetCode --- 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);
示例2:
输入: s = "LEETCODEISHIRING", numRows = 4
输出: "LDREOEIIECIHNTSG"
解释:
L D R
E O E I I
E C I H N
T S G
解题方法
对于n行的Z字形字符序列,可以发现每间隔(2*n-2)个序号为一个周期,假设考虑字符串序号从1开始向后排列,那么
第2n-1个字符和第1个字符都在第一行、
第2n个字符和第2个字符都在第二行、
第2n+1个字符和第3个字符都在第三行、
……
第3n-2个字符和第n个字符都在第n行。
Z字形字符序列的每一行都可以看做一个列表,初始化一个列表res = [[],[],[],...,[]]
,列表有n个元素,每个元素都是一个子列表,子列表内容是按顺序出现在对应行的字符。
那么关键需要找到字符串每个序号对应Z字形字符序列的行号,定义一个函数为int Z_func(int numRows, int index);
,返回字符numRows[index-1]在字符串numRows对应Z字形字符序列的行号(1~numRows)。
上图假设n=5,序号16先被模8(2n-2)得到0,但我们期望它序号是8(8 = 0 = 16 mod 8)以方便后面判断,用8-n取绝对值得到3,表示点16与Z字形最下层的距离distance,然后再用n反向减去distance取绝对值得到5-3=2,就表示了行号2。
再假设序号为15的时候先被模8(2n-2)得到7,用7-n取绝对值得到2,表示点15与Z字形最下层的距离distance,然后再用n反向减去distance取绝对值得到5-2=3,就表示了行号3。
此外还有两种特殊情况:行数n为1或字符串s长度为1,此时都只需要直接返回字符串s就可以。
程序
class Solution:
def Z_func(self, numRows: int, index: int) -> int:
maxlen = 2*numRows - 2
index %= maxlen
if index == 0:
index += maxlen
distance = abs(numRows - index)
return abs(numRows - distance)
def convert(self, s: str, numRows: int) -> str:
if len(s) == 1 or numRows == 1:
return s
res = []
for i in range(numRows):
res.append([])
for index,value in enumerate(s):
line = self.Z_func(numRows, index + 1)
res[line-1].append(value)
return "".join([j for i in res for j in i])