6. Zigzag Conversion

6. Zigzag Conversion

The string "PAYPALISHIRING" is written in a zigzag pattern on a given number of rows like this: (you may want to display this pattern in a fixed font for better legibility)

 

P   A   H   N
A P L S I I G
Y   I   R

And then read line by line: "PAHNAPLSIIGYIR"

Write the code that will take a string and make this conversion given a number of rows:

 

string convert(string s, int numRows);

Example 1:

 

Input: s = "PAYPALISHIRING", numRows = 3
Output: "PAHNAPLSIIGYIR"

Example 2:

 

Input: s = "PAYPALISHIRING", numRows = 4
Output: "PINALSIGYAHRPI"
Explanation:
P     I    N
A   L S  I G
Y A   H R
P     I

Example 3:

 

Input: s = "A", numRows = 1
Output: "A"

Constraints:

 

  • 1 <= s.length <= 1000
  • s consists of English letters (lower-case and upper-case), ',' and '.'.
  • 1 <= numRows <= 1000

 

解决思路

观察上面的例子,我们发现最后的输出是第一行的字母加上第二行的字母,一直加到最后一行的字母。那我们就考虑是不是可以建立numRows个临时空字符串,我们只要将字符串s中的每个字符,正确的插入到所在行对应的临时字符串的中,最后将这numRows个临时字符串拼接返回就可以了。

比如以上面那个示例为例,numRows=4,应该建立4个临时空字符串,L是第一个字符,则应该加入到第一个临时字符串中,E是第二个字符,应该加入到第二个临时字符串中,…,C是第五个字符,应该加入到第三个临时字符串中,…,一直到最后一个字符G加入到了第四个临时字符串中,最终将这四个临时字符串拼接到一起,则为我们需要的输出结果。

那我们问题的关键就变成了,如何确定字符串s中的每个字符所对应的行数,也就是说字符串s中的字符应该加入到第几个临时字符串中。

我们观察上面的两个示例,我们看看能不能发现如何确定字符对应行数的方法。

在这里插入图片描述在这里插入图片描述
如上图所示,我们可以发现,进行Z字排列的时候,我们总是先向下排列numRows个字符,再从左向右从下到上排列numRows-2个字符,接下来,循环往复,直到我们最终完成所有字符的排列。则我们可以把排列的过程看成一个一个块的堆叠,即先排满前一个块之后才能排列下一个块,那么我们就可以很容易的找出每个字符所对应的行数。

思路1

根据我们上面的分析,我们排列字符串s中的字符的时候,是一个块一个块的进行排列,那我们可以得到,当一个块填满的时候,其容量为2*numRows-2。后面块中字符与第一个块对应位置的字符相差n个(2*numRows-2)的大小,那我们可以设置一个字典,确定第一个块中字符所在的行数,则后面所有的块的内容,可以通过减去n*(2*numRows-2)来映射到我们的字典中,获得其所在的行数。

我们可以写出完成的代码为:

class Solution:
    def convert(self, s, numRows):
        if numRows <= 1:
            return s

        n = 2*numRows-2
        temp_str = ['']*numRows
        temp_dict = {
    }

        for t in range(n):
            if t>=0 and t<numRows:
                temp_dict[t] = t
            else:
                temp_dict[t] = n-t
        
        for i in range(len(s)):
            temp_str[temp_dict[i%n]] += s[i]

        return ''.join(temp_str)

结果 80ms 时间复杂度O(n)

思路二 1

继续回到刚刚的分析过程,我们将排列的过程变成了一个一个块的填充,我们在填充一个块的时候,先向下排numRows个,然后再向上排列numRows-2个。

向下排是从第0行一直排到第numRows-1行,即可以用for j in range(numRows)表示

向上排列的过程可以看成一个倒序排列的过程,我们注意到倒序排列时,起始行是第numRows-2行(第numRows02行填充),结束位置是第0行(第0行不填充),则start=numRows-2,end=0,step=-1,则倒序排列为for k in range(numRows-2,0,-1)

这样对于一个块中的字符的填充我们就解决了,先for j in range(numRows),再for k in range(numRows-2,0,-1),后续块与这个块采用同样的方法便可以解决

如果上面的没搞清楚,看了下面的代码可以很快的搞明白了:

class Solution:
    def convert(self, s, numRows):
        if not numRows > 1:
            return s
        if numRows == 2:   #做了一个特例处理
            return s[::2]+s[1::2]

        temp_str = ['']*numRows
        i = 0
        n = len(s)
        while i < n:
            for j in range(numRows):
                if i < n:
                    temp_str[j] += s[i]  
                    i += 1
       
            for k in range(numRows - 2, 0, -1):  
                if i < n:
                    temp_str[k] += s[i]
                    i += 1
        return ''.join(temp_str)

结果 64ms 时间复杂度O(n)


  1. darede在leetcode题解中回答 :https://leetcode-cn.com/problems/zigzag-conversion/solution/6-zzi-bian-huan-python3-an-xing-qu-zhi-by-darede/
class Solution:
    def convert(self, s: str, numRows: int) -> str:
        print(s)
        print(numRows)
        if len(s) <= 1 or numRows == 1:
            return s
        
        n = numRows * 2 - 2

        tmp_dict = {}
        tmp_str = [''] * numRows

        for i in range(n):
            if i >=0 and i < numRows:
                tmp_dict[i] = i
            else:
                tmp_dict[i] = n - i
        print(tmp_dict)
        m = len(s)
        for j in range(m):
            print(j)
            print(j % n)
            tmp_row_index = tmp_dict[j % n]
            print(tmp_row_index)
            tmp_str[tmp_row_index] += s[j]
            print("-------------------")

        return ''.join(tmp_str)
No Comments

Post A Comment