6-Z字形变换

题目描述

将一个给定字符串 s 根据给定的行数 numRows ,以从上往下、从左到右进行 Z 字形排列。

比如输入字符串为 “PAYPALISHIRING” 行数为 3 时,排列如下:

PAHN
APLSIIG
YIR

之后,你的输出需要从左往右逐行读取,产生出一个新的字符串,比如:“PAHNAPLSIIGYIR”。
示例 1:

输入:s = “PAYPALISHIRING”, numRows = 3
输出:“PAHNAPLSIIGYIR”

示例 2:
输入:s = “PAYPALISHIRING”, numRows = 4
输出:“PINALSIGYAHRPI”

PIN
ALSIG
YAHR
PI

示例 3:

输入:s = “A”, numRows = 1
输出:“A”

提示:
1 <= s.length <= 1000
s 由英文字母(小写和大写)、‘,’ 和 ‘.’ 组成
1 <= numRows <= 1000

解题思路

思路一:过程模拟

定义numRows个数组,用于存放每一行的所有字符,然后直接模拟排列过程,最后先按行连接再按列连接即可,空间复杂度较高。
易错点在于,当numRows == 1或numRows >= s.length时,需要特殊处理,直接返回s。

class Solution:
    def convert(self, s: str, numRows: int) -> str:
        if numRows == 1 or numRows >= len(s):
            return s
        all = [[] for i in range(numRows)]
        row, dire = 0, 1
        for i in range(len(s)):
            all[row].append(s[i])
            if row == numRows-1:
                dire = -1
            elif row == 0:
                dire = 1
            row += dire
            
        res = ""
        for i in range(numRows):
            res += "".join(all[i])
        
        return res

思路二:寻找排列规律

同一行的数字存在一定的规律,如果找到这个规律,那么只需要一次遍历就能够完成题解。
先来看看示例1,数字代表字符串的下标。

04812
135791113
2610

第一行:0,4,8,12(依次增4)
第二行:1,3,5,7,9,11,13(先增2再增2,2+2=4)
第三行:2,6,10(依次增4)

再来看看示例2,同上。

0612
1571113
24810
39

第一行:0,6,12(依次增6)
第二行:1,5,7,11,13(先增4再增2,4+2=6)
第三行:2,4,8,10(先增2,再增4,2+4=6)
第四行:3,9(依次增6)

可以推得,排列的周期为T = 2*numRows-2。
对于第零行,每个周期内只产生一个字符,且字符的坐标x满足x%T=0。
对于第 i 行(0<i<numRows-1),每个周期产生两个字符,且第一个字符的坐标x满足x%T=i,第二个字符的坐标y满足x%T=T-i。
对于最后一行,每个周期产生一个字符,且字符的坐标x满足x%T=numRows-1。

if numRows == 1 or numRows >= len(s):
            return s
        
        T = 2*numRows-2
        ans = ["" for i in range(numRows)]
        for x in range(len(s)):
            mod = x%T
            if 0 <= mod <= numRows-1:
                ans[mod] += s[x]
            else:
                ans[T-mod] += s[x]
        return "".join(ans)

官方题解

思路一

class Solution:
    def convert(self, s: str, numRows: int) -> str:
        n, r = len(s), numRows
        if r == 1 or r >= n:
            return s
        t = r * 2 - 2
        c = (n + t - 1) // t * (r - 1)
        mat = [[''] * c for _ in range(r)]
        x, y = 0, 0
        for i, ch in enumerate(s):
            mat[x][y] = ch
            if i % t < r - 1:
                x += 1  # 向下移动
            else:
                x -= 1
                y += 1  # 向右上移动
        return ''.join(ch for row in mat for ch in row if ch)
# 时间复杂度:O(r⋅n),其中r=numRows,n 为字符串 s 的长度。时间主要消耗在矩阵的创建和遍历上,矩阵的行数为r,列数可以视为O(n)。
# 空间复杂度:O(r⋅n)。矩阵需要O(r⋅n) 的空间。

# 矩阵压缩
class Solution:
    def convert(self, s: str, numRows: int) -> str:
        n, r = len(s), numRows
        if r == 1 or r >= n:
            return s
        t = r * 2 - 2
        c = (n + t - 1) // t * (r - 1)
        mat = [[''] * c for _ in range(r)]
        x, y = 0, 0
        for i, ch in enumerate(s):
            mat[x][y] = ch
            if i % t < r - 1:
                x += 1  # 向下移动
            else:
                x -= 1
                y += 1  # 向右上移动
        return ''.join(ch for row in mat for ch in row if ch)

# 时间复杂度:O(n)。
# 空间复杂度:O(n)。压缩后的矩阵需要 O(n) 的空间。

思路二

class Solution:
    def convert(self, s: str, numRows: int) -> str:
        n, r = len(s), numRows
        if r == 1 or r >= n:
            return s
        t = r * 2 - 2
        ans = []
        for i in range(r):  # 枚举矩阵的行
            for j in range(0, n - i, t):  # 枚举每个周期的起始下标
                ans.append(s[j + i])  # 当前周期的第一个字符
                if 0 < i < r - 1 and j + t - i < n:
                    ans.append(s[j + t - i])  # 当前周期的第二个字符
        return ''.join(ans)
# 时间复杂度:O(n),其中 n 为字符串 s 的长度。s 中的每个字符仅会被访问一次,因此时间复杂度为O(n)。
# 空间复杂度:O(1)。返回值不计入空间复杂度。

凉梦空间

欢迎你进入我的个人博客网站参观交流:https://www.liangmeng.xyz

在这里插入图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

凉丶梦

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值