1. 程式人生 > 實用技巧 >LeetCode --- Z字形變換

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,序號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])