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个字符都在第一行、
第2
n个字符和第2个字符都在第二行、
第2n+1个字符和第3个字符都在第三行、
……
第3
n-2个字符和第n个字符都在第n行。

Z字形字符序列的每一行都可以看做一个列表,初始化一个列表res = [[],[],[],...,[]],列表有n个元素,每个元素都是一个子列表,子列表内容是按顺序出现在对应行的字符。
那么关键需要找到字符串每个序号对应Z字形字符序列的行号,定义一个函数为int Z_func(int numRows, int index);,返回字符numRows[index-1]在字符串numRows对应Z字形字符序列的行号(1~numRows)。
n=5 序号示意图
上图假设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(2
n-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])
posted @ 2020-08-08 17:16  爱吃砂糖橘的白龙  阅读(160)  评论(0编辑  收藏  举报