作者: admin2025

  • The Maze,LeetCode,490

    题目描述

    由空地和墙组成的迷宫中有一个球。球可以向上(u)、下(d)、左(l)或右(r)四个方向滚动,但在遇到墙壁前不会停止滚动。当球停下时,可以选择下一个方向。迷宫中有一个终点,当球到达终点时,游戏结束。

    给定迷宫的二维网格,其中 1 表示墙壁, 表示空地,以及球的起始位置和终点位置,请判断球能否到达终点。

    示例

    输入 1: 迷宫由以下二维数组表示

    0 0 1 0 0 0 0 0 0 0 0 0 0 1 0 1 1 0 1 1 0 0 0 0 0

    输入 2: 球的初始位置 (rowStart, colStart) = (0, 4) 输入 3: 球的终点位置 (rowEnd, colEnd) = (4, 4)

    输出: true

    解释: 球可以按照如下路径从起点滚到终点: (0, 4) -> (0, 3) -> (1, 3) -> (1, 2) -> (1, 1) -> (1, 0) -> (2, 0) -> (2, 1) -> (2, 2) -> (3, 2) -> (4, 2) -> (4, 3) -> (4, 4)

    解题思路

    1. 深度优先搜索(DFS)
      • 从起始位置开始,尝试四个方向滚动。
      • 每个方向上,球会一直滚动直到遇到墙壁或边界。
      • 到达新位置后,递归地继续搜索。
      • 如果到达终点,返回 true
      • 使用一个集合记录已经访问过的位置,避免重复搜索。
    2. 广度优先搜索(BFS)
      • 使用队列存储待访问的位置。
      • 从起始位置开始,将四个方向上的可行位置加入队列。
      • 每次从队列中取出一个位置,检查是否到达终点。
      • 如果到达终点,返回 true
      • 否则,继续将新位置加入队列。

    代码实现(DFS)

    def hasPath(maze, start, destination): def dfs(maze, start, destination, visited): if start == destination: return True visited.add(start) directions = [(0, 1), (1, 0), (0, -1), (-1, 0)] for dx, dy in directions: x, y = start while 0 <= x + dx < len(maze) and 0 <= y + dy < len(maze[0]) and maze[x + dx][y + dy] == 0: x += dx y += dy if (x, y) not in visited and dfs(maze, (x, y), destination, visited): return True return False

    visited = set()
    return dfs(maze, tuple(start), tuple(destination), visited)

    示例输入

    maze = [ [0, 0, 1, 0, 0], [0, 0, 0, 0, 0], [0, 0, 0, 1, 0], [1, 1, 0, 1, 1], [0, 0, 0, 0, 0] ] start = [0, 4] destination = [4, 4]

    调用函数

    print(hasPath(maze, start, destination)) # 输出: True

    代码实现(BFS)

    from collections import deque

    def hasPath(maze, start, destination): queue = deque([tuple(start)]) visited = set([tuple(start)]) directions = [(0, 1), (1, 0), (0, -1), (-1, 0)]

    while queue:
        x, y = queue.popleft()
        if (x, y) == tuple(destination):
            return True
        for dx, dy in directions:
            nx, ny = x, y
            while 0 <= nx + dx < len(maze) and 0 <= ny + dy < len(maze[0]) and maze[nx + dx][ny + dy] == 0:
                nx += dx
                ny += dy
            if (nx, ny) not in visited:
                visited.add((nx, ny))
                queue.append((nx, ny))
    return False

    示例输入

    maze = [ [0, 0, 1, 0, 0], [0, 0, 0, 0, 0], [0, 0, 0, 1, 0], [1, 1, 0, 1, 1], [0, 0, 0, 0, 0] ] start = [0, 4] destination = [4, 4]

    调用函数

    print(hasPath(maze, start, destination)) # 输出: True

    总结

    • DFS 适合于路径搜索,但可能会遇到深度很大的情况,导致栈溢出。
    • BFS 适合于寻找最短路径,且不会因为深度过大而出现问题,但需要更多的空间来存储队列。

    根据具体问题的需求选择合适的算法。在这个迷宫问题中,两种方法都可以有效解决。

  • Robot Room Cleaner,LeetCode,489

    好的,我们来详细讨论一下 LeetCode 上的第 489 题:Robot Room Cleaner。

    题目描述

    这道题要求我们模拟一个机器人在一个未知的房间内进行清洁的过程。房间的布局是未知的,机器人只能通过有限的方向移动(通常是上、下、左、右),并且机器人不能回到已经清洁过的位置。

    机器人有一个 clean 方法用来清洁当前位置,以及一个 move 方法用来移动到下一个位置。如果移动失败(比如遇到了墙壁),move 方法会返回 false。机器人还有一个 turnLeftturnRight 方法用来改变方向。

    解题思路

    这道题可以使用深度优先搜索(DFS)来解决这个问题。关键点在于如何避免重复访问已经清洁过的位置,以及如何确保机器人能够回到出发点。

    1. 方向控制:定义一个方向数组,比如 [(0, 1), (1, 0), (0, -1), (-1, 0)],分别代表上、右、下、左四个方向。
    2. 状态记录:使用一个集合来记录已经清洁过的位置。
    3. 回溯:在每次移动到一个新位置后,进行清洁,并递归地探索其他方向。探索完所有方向后,需要回到上一个位置。

    代码实现

    以下是一个 Python 示例代码,假设机器人类已经定义好了 clean, move, turnLeft, turnRight 方法。

    class Solution: def cleanRoom(self, robot): def go_back(): robot.turnRight() robot.turnRight() robot.move() robot.turnRight() robot.turnRight()

        def dfs(x, y, d):
            robot.clean()
            visited.add((x, y))
    
            for i in range(4):
                new_d = (d + i) % 4
                new_x, new_y = x + directions[new_d][0], y + directions[new_d][1]
    
                if (new_x, new_y) not in visited and robot.move():
                    dfs(new_x, new_y, new_d)
                    go_back()
    
                robot.turnRight()
    
        directions = [(0, 1), (1, 0), (0, -1), (-1, 0)]
        visited = set()
        dfs(0, 0, 0)

    假设机器人类如下:

    class Robot: def clean(self): pass

    def move(self):
        pass
    
    def turnLeft(self):
        pass
    
    def turnRight(self):
        pass

    详细解释

    1. go_back 函数:这个函数用于让机器人回到上一个位置。具体操作是先转180度,然后移动一步,再转回原来的方向。
    2. dfs 函数:这是一个深度优先搜索函数,参数 (x, y, d) 分别表示当前的位置和方向。
      • 首先清洁当前位置,并将当前位置加入 visited 集合。
      • 然后尝试四个方向(通过循环和方向数组 directions 实现)。
      • 如果新位置没有被访问过且可以移动,递归调用 dfs
      • 每次尝试新方向前,先右转,以便下一次循环时尝试下一个方向。
    3. 主函数:初始化方向数组和访问集合,从 (0, 0, 0)(即起始位置和向上方向)开始进行深度优先搜索。

    注意事项

    • 机器人类的具体实现细节(如 clean, move, turnLeft, turnRight 方法)需要根据题目提供的接口来调整。
    • 这道题的关键在于理解和实现回溯过程,确保机器人能够回到出发点。

    希望这个详细的解答能帮助你理解并解决这道题目。如果有更多问题,欢迎继续提问!

  • Zuma Game,LeetCode,488

    《Zuma Game》(祖玛游戏)是 LeetCode 上的第 488 题,这是一个中等难度的题目,主要考察的是深度优先搜索(DFS)和广度优先搜索(BFS)的算法应用。

    题目描述

    在一个二维的平面中,有一些彩球按顺序排列成一行,你需要通过射出彩球来消除这些彩球。当三个或更多相同颜色的彩球相连时,这些彩球会被消除。你需要找到消除所有彩球所需的最少射击次数。

    输入:

    • board:一个字符串,表示初始彩球的排列。
    • hand:一个字符串,表示你手中持有的彩球。

    输出:

    • 返回消除所有彩球所需的最少射击次数。如果无法消除所有彩球,则返回 -1。

    示例

    输入:board = "WRRBBW", hand = "RB" 输出:-1 解释:无法通过手中的彩球消除所有彩球。

    输入:board = "WWRRBBWW", hand = "WRBRW" 输出:2 解释:可以通过以下步骤消除所有彩球:

    1. 射击手中的 'R' 到位置 3,使得 "WWRRBBWW" 变为 "WWRRRBBWW"。
    2. 射击手中的 'B' 到位置 6,使得 "WWRRRBBWW" 变为 "WWRRRBBBW"。 这时,三个 'R' 和三个 'B' 相连,被消除,剩下的 "WWW" 也会被自动消除。

      解题思路

    深度优先搜索(DFS)

    • 从左到右遍历 board,找到第一个可以插入的位置。
    • 尝试将手中的彩球插入到该位置,并递归地进行下一步搜索。
    • 每次插入后,检查是否有连续的三个或更多相同颜色的彩球,如果有则消除它们,并继续递归搜索。
    • 记录每次搜索的结果,找到最少射击次数。

  • 广度优先搜索(BFS)
    • 使用队列来存储当前的状态(当前的 board 和手中的 hand)。
    • 从初始状态开始,逐层扩展状态,每次尝试将手中的彩球插入到 board 中的不同位置。
    • 检查是否有连续的三个或更多相同颜色的彩球,如果有则消除它们,并更新状态。
    • 记录每层扩展的次数,找到最少射击次数。
  • 代码实现(DFS 示例)

    class Solution: def findMinStep(self, board: str, hand: str) -> int: from collections import Counter

        def clean_board(b):
            # 消除连续的三个或更多相同颜色的彩球
            j = 0
            while j < len(b):
                k = j
                while k < len(b) and b[k] == b[j]:
                    k += 1
                if k - j >= 3:
                    b = b[:j] + b[k:]
                    j = 0
                else:
                    j = k
            return b
    
        def dfs(b, h):
            if not b:
                return 0
            b = clean_board(b)
            cnt = Counter(h)
            res = float('inf')
            j = 0
            while j < len(b):
                k = j
                while k < len(b) and b[k] == b[j]:
                    k += 1
                need = 3 - (k - j)
                if cnt[b[j]] >= need:
                    cnt[b[j]] -= need
                    res = min(res, dfs(b[:j] + b[k:], ''.join(cnt.elements())) + need)
                    cnt[b[j]] += need
                j = k
            return res
    
        result = dfs(board, hand)
        return result if result != float('inf') else -1

    示例使用

    sol = Solution() print(sol.findMinStep("WWRRBBWW", "WRBRW")) # 输出:2 print(sol.findMinStep("WRRBBW", "RB")) # 输出:-1

    关键点

    1. 递归与回溯:通过递归尝试每一种可能的插入方式,并在回溯时恢复状态。
    2. 状态清理:在每次插入后,需要清理 board 中的连续相同颜色的彩球。
    3. 剪枝优化:如果当前状态无法继续消除彩球,则提前返回,避免无效搜索。

    通过以上思路和代码实现,可以有效地解决 LeetCode 488 题《Zuma Game》。希望这个详细的解答对你有所帮助!如果有更多问题,欢迎继续提问。

  • Max Consecutive Ones II,LeetCode,487

    题目描述:

    给定一个二进制数组,你可以最多翻转一个 1,求最长连续 1 的个数。

    示例:

    输入: [1,0,1,1,0] 输出: 4 解释: 翻转第二个 0 可以得到最长的连续 1 的序列。

    解题思路:

    这道题可以使用滑动窗口的技巧来解决。滑动窗口的思路是维护一个窗口,这个窗口内最多包含一个 ,然后计算这个窗口的长度。

    具体步骤:

    1. 初始化变量:
      • leftright 指针,表示窗口的左右边界。
      • zero_count 记录窗口内 的个数。
      • max_len 记录最长连续 1 的长度。
    2. 滑动窗口:
      • 使用 right 指针遍历数组。
      • 如果当前元素是 zero_count 增加。
      • 如果 zero_count 超过 1,说明窗口内有两个 ,需要移动 left 指针,直到 zero_count 重新变为 1
      • 在每次移动 right 指针后,更新 max_lenright - left + 1
    3. 返回结果:
      • 遍历结束后,max_len 就是所求的最长连续 1 的长度。

    代码实现:

    def findMaxConsecutiveOnes(nums): left = 0 zero_count = 0 max_len = 0

    for right in range(len(nums)):
        if nums[right] == 0:
            zero_count += 1
    
        while zero_count > 1:
            if nums[left] == 0:
                zero_count -= 1
            left += 1
    
        max_len = max(max_len, right - left + 1)
    
    return max_len

    示例

    nums = [1, 0, 1, 1, 0] print(findMaxConsecutiveOnes(nums)) # 输出: 4

    解释:

    • leftright 指针用于维护滑动窗口的边界。
    • zero_count 用于记录当前窗口内 的个数。
    • zero_count 超过 1 时,说明窗口内有两个 ,需要移动 left 指针,直到窗口内只有一个
    • 每次移动 right 指针后,更新 max_len 为当前窗口的长度。

    复杂度分析:

    • 时间复杂度: O(n),其中 n 是数组的长度。每个元素被访问两次(一次是 right 指针,一次是 left 指针)。
    • 空间复杂度: O(1),使用了常数个额外空间。

    这种方法高效且直观,利用滑动窗口的技巧很好地解决了问题。希望这个解释对你有所帮助!如果有其他问题,欢迎继续提问。

  • Predict the Winner,LeetCode,486

    LeetCode 486题 “Predict the Winner” 是一个中等难度的动态规划问题。题目要求预测两个玩家在玩一个游戏时的胜者。游戏规则如下:

    1. 给定一个整数数组 nums
    2. 两个玩家轮流从数组的两端选择一个数字,并将其从数组中移除。
    3. 每个玩家总是尽可能使自己的得分最大化。
    4. 玩家的得分是他们选择的数字的总和。
    5. 预测玩家1(先手)是否能赢得游戏,或者两人是否会打成平手。

    解题思路

    我们可以使用动态规划来解决这个问题。定义一个二维数组 dp,其中 dp[i][j] 表示在子数组 nums[i...j] 上,先手玩家能获得的最大分数与后手玩家能获得的最大分数之差。

    状态转移方程

    • i == j 时,只有一个数字可以选择,dp[i][j] = nums[i]
    • i < j 时,先手玩家有两种选择:
      • 选择 nums[i],则后手玩家在 nums[i+1...j] 上成为先手,此时先手玩家的得分差为 nums[i] - dp[i+1][j]
      • 选择 nums[j],则后手玩家在 nums[i...j-1] 上成为先手,此时先手玩家的得分差为 nums[j] - dp[i][j-1]
  • Max Consecutive Ones,LeetCode,485

    好的,我们来详细讲解一下 LeetCode 上的第 485 题:Max Consecutive Ones(最大连续1的个数)。

    题目描述

    给定一个二进制数组,返回该数组中连续1的最大个数。

    示例 1:

    输入: [1,1,0,1,1,1] 输出: 3 解释: 开头的两位和最后的三位都是连续1,所以最大连续1的个数是 3.

    解题思路

    这个问题可以通过一次遍历数组来解决。我们可以使用两个变量:

    1. max_count:用来记录最大的连续1的个数。
    2. current_count:用来记录当前连续1的个数。

    遍历数组时,如果遇到1,就将current_count加1;如果遇到0,就将current_count重置为0,并将max_count更新为current_countmax_count中的较大值。

    具体步骤

    1. 初始化max_countcurrent_count为0。
    2. 遍历数组中的每一个元素:
      • 如果元素为1,current_count加1。
      • 如果元素为0,比较current_countmax_count,更新max_count为较大值,然后将current_count重置为0。
    3. 遍历结束后,再次比较current_countmax_count,以确保最后一个连续1的序列被考虑进去。
    4. 返回max_count

    代码实现

    以下是使用Python实现的代码:

    class Solution: def findMaxConsecutiveOnes(self, nums): max_count = 0 current_count = 0

        for num in nums:
            if num == 1:
                current_count += 1
            else:
                max_count = max(max_count, current_count)
                current_count = 0
    
        # 最后一次检查,以防数组以连续1结尾
        max_count = max(max_count, current_count)
    
        return max_count

    复杂度分析

    • 时间复杂度: O(n),其中n是数组的长度。我们只需要遍历一次数组。
    • 空间复杂度: O(1),我们只使用了常数个额外空间。

    测试用例

    sol = Solution() print(sol.findMaxConsecutiveOnes([1,1,0,1,1,1])) # 输出: 3 print(sol.findMaxConsecutiveOnes([1,0,1,1,0,1])) # 输出: 2 print(sol.findMaxConsecutiveOnes([0,0,0,0,0,0])) # 输出: 0 print(sol.findMaxConsecutiveOnes([1,1,1,1,1,1])) # 输出: 6

    通过这些测试用例,我们可以验证代码的正确性。

    希望这个详细的解答对你有帮助!如果有其他问题,欢迎继续提问。

  • Find Permutation,LeetCode,484

    题目描述:

    在 LeetCode 上,第 484 题的标题是 “Find Permutation”,中文可以翻译为“寻找排列”。题目描述如下:

    给定一个由若干个 “I”(表示上升)和 “D”(表示下降)组成的字符串 s,你需要构造一个长度为 n 的排列 p,其中 n 是字符串 s 的长度加 1。排列 p 需要满足以下条件:

    • p 是一个从 1 到 n 的整数排列。
    • 对于每个 i(0 ≤ i < n - 1),如果 s[i] 是 “I”,则 p[i] < p[i+1];如果 s[i] 是 “D”,则 p[i] > p[i+1]

    你需要返回所有符合条件的排列 p

    示例:

    输入: "IDID" 输出: [1,4,3,2,5]

    解题思路:

    1. 初始化: 创建一个长度为 n 的数组 p,初始值为 1 到 n 的顺序排列。
    2. 处理 “D” 区间: 遍历字符串 s,找到连续的 “D” 区间,将这些区间内的数字进行逆序排列,以满足下降的要求。
    3. 返回结果: 经过处理后的数组 p 即为符合条件的排列。

    详细代码实现:

    class Solution: def findPermutation(self, s: str) -> List[int]: n = len(s) + 1 p = list(range(1, n + 1)) # 初始化排列

        i = 0
        while i < len(s):
            if s[i] == 'D':
                start = i
                # 找到连续的 'D' 区间
                while i < len(s) and s[i] == 'D':
                    i += 1
                # 将区间 [start, i] 的数字逆序
                p[start:i+1] = reversed(p[start:i+1])
            else:
                i += 1
    
        return p

    解释:

    1. 初始化排列: p = list(range(1, n + 1)) 创建一个从 1 到 n 的顺序排列。
    2. 遍历字符串 s
      • 如果遇到 “D”,记录起始位置 start,继续遍历直到 “D” 结束的位置 i
      • 使用 reversed(p[start:i+1]) 将区间 [start, i] 的数字逆序,以满足下降的要求。
    3. 返回结果: 经过处理后的 p 即为符合条件的排列。

    复杂度分析:

    • 时间复杂度: O(n),其中 n 是字符串 s 的长度。我们需要遍历字符串 s 并对某些区间进行逆序操作,逆序操作的复杂度为 O(k),k 是区间的长度,总和不超过 n。
    • 空间复杂度: O(n),用于存储排列 p

    这个解法简洁且高效,能够满足题目要求,生成符合条件的排列。希望这个详细的解释对你有所帮助!如果有任何进一步的问题,欢迎继续提问。

  • Smallest Good Base,LeetCode,483

    LeetCode 483题 “Smallest Good Base” 是一个中等难度的数学问题。题目要求找到一个最小的“好基数”(good base),使得给定的正整数 ( N ) 可以表示为以下形式:

    [ N = a^0 + a^1 + a^2 + \ldots + a^d ]

    其中 ( a ) 是基数,( d ) 是指数的最大值,且 ( a ) 和 ( d ) 都是正整数。

    题目描述

    给定一个正整数 ( N ),找到最小的“好基数” ( a )。

    解题思路

    1. 数学转换
      • 首先,将等式转换为数学形式: [ N = \frac{a^{d+1} – 1}{a – 1} ]
      • 这个等式表示 ( N ) 可以被表示为 ( a ) 的等比数列之和。
    2. 确定范围
      • 对于给定的 ( N ),我们需要找到合适的 ( d ) 和 ( a )。
      • ( d ) 的最大值可以通过 ( 2^d \leq N ) 来估算,因为 ( a ) 至少为2。
    3. 二分查找
      • 对于每个可能的 ( d ),使用二分查找来确定合适的 ( a )。
      • 二分查找的范围是 ( [2, N^{1/d}] )。
    4. 验证
      • 对于每个 ( a ),验证等式 ( N = \frac{a^{d+1} – 1}{a – 1} ) 是否成立。

    代码实现

    以下是 Python 的实现代码:

    class Solution: def smallestGoodBase(self, N: int) -> str: import math

        def check(a, d):
            # 检查是否满足等式 N = a^0 + a^1 + ... + a^d
            sum = 0
            for i in range(d + 1):
                sum = sum * a + 1
            return sum == N
    
        def find_a(d):
            # 使用二分查找找到合适的 a
            left, right = 2, int(N**(1/d)) + 1
            while left < right:
                mid = (left + right) // 2
                sum = (mid**(d+1) - 1) // (mid - 1)
                if sum == N:
                    return mid
                elif sum < N:
                    left = mid + 1
                else:
                    right = mid
            return -1
    
        # 从大的 d 开始尝试,找到最小的 a
        for d in range(int(math.log2(N)), 1, -1):
            a = find_a(d)
            if a != -1:
                return str(a)
    
        # 如果没有找到,返回 N-1(根据题意,这种情况不会发生)
        return str(N - 1)

    示例

    sol = Solution() print(sol.smallestGoodBase(13)) # 输出 "3" print(sol.smallestGoodBase(4681)) # 输出 "8"

    解释

    1. check函数
      • 用于验证给定的 ( a ) 和 ( d ) 是否满足等式。
    2. find_a函数
      • 使用二分查找来确定合适的 ( a )。
    3. 主函数
      • 从大的 ( d ) 开始尝试,找到最小的 ( a )。
      • ( d ) 的范围从 ( \log_2(N) ) 开始向下递减,因为 ( d ) 越大,( a ) 越小。

    复杂度分析

    • 时间复杂度:( O(\log^2(N)) ),因为 ( d ) 的范围是 ( O(\log(N)) ),每次二分查找的时间复杂度是 ( O(\log(N)) )。
    • 空间复杂度:( O(1) ),使用了常数空间。

    通过上述步骤和代码实现,可以有效地解决 LeetCode 483题 “Smallest Good Base”。

  • License Key Formatting,LeetCode,482

    题目描述(LeetCode 482):License Key Formatting

    你有一个密钥字符串 S,其中包含字母、数字和短划线 -。你需要将这个字符串重新格式化为一个由字母和数字组成的字符串,并且每 K 个字符之间用短划线分隔。

    具体要求如下:

    1. 字符串 S 中只包含字母(大写和小写)、数字和短划线 -
    2. 你需要将 S 中的短划线去掉,并将剩余的字符重新格式化。
    3. 格式化后的字符串中,每 K 个字符之间用短划线分隔,最后一个分组可能不满 K 个字符。

    示例:

    输入:S = "5F3Z-2e-9-w", K = 4

    输出:"5F3Z-2E9W"

    解释:字符串 S 中的短划线被去掉,剩余的字符按每 4 个一组重新格式化,得到 "5F3Z-2E9W"

    解题思路:

    1. 去除短划线:首先将字符串 S 中的所有短划线去掉。
    2. 反转字符串:为了方便从后往前每 K 个字符分组,可以将字符串反转。
    3. 分组并添加短划线:从反转后的字符串开始,每 K 个字符一组,添加短划线。
    4. 再次反转:将分组后的字符串再次反转,得到最终结果。

    代码实现(Python):

    class Solution: def licenseKeyFormatting(self, S: str, K: int) -> str:

    去除短划线并转换为大写

        S = S.replace('-', '').upper()
    
        # 反转字符串
        S = S[::-1]
    
        # 分组并添加短划线
        result = []
        for i in range(0, len(S), K):
            result.append(S[i:i+K])
    
        # 再次反转并连接成最终结果
        formatted_key = '-'.join(result)[::-1]
    
        return formatted_key

    示例使用

    sol = Solution() print(sol.licenseKeyFormatting("5F3Z-2e-9-w", 4)) # 输出: "5F3Z-2E9W"

    详细解释:

    1. 去除短划线并转换为大写S = S.replace('-', '').upper() 这一步将输入字符串中的所有短划线去掉,并将所有字符转换为大写,以便统一格式。
    2. 反转字符串S = S[::-1] 反转字符串是为了方便从后往前每 K 个字符进行分组。
    3. 分组并添加短划线result = [] for i in range(0, len(S), K): result.append(S[i:i+K]) 通过循环,每 K 个字符一组,将分组后的字符串添加到结果列表中。
    4. 再次反转并连接成最终结果formatted_key = '-'.join(result)[::-1] 将分组后的字符串列表用短划线连接起来,并再次反转,得到最终的格式化后的密钥字符串。

    这种方法的时间复杂度为 O(N),其中 N 是字符串 S 的长度,空间复杂度也为 O(N),主要用于存储中间结果和最终结果。

  • Magical String,LeetCode,481

    题目描述:

    神奇字符串 s 仅由 '1''2' 组成,并需要满足以下条件:

    1. 字符串 s 是神奇的,因为串联字符串 s 中的 '1''2' 组成的字符串等于 s 本身。
    2. 对于每个索引 is[0] 的索引视为 ),s[i] 表示 s 中索引 i 的字符应该出现的次数。

    给你一个整数 n,返回在神奇字符串 s 的前 n 个字符中有多少个 '1'

    示例:

    输入:n = 6 输出:3 解释:神奇字符串 s 的前 6 个字符是 “122112”,其中 “1” 出现了 3 次。

    解题思路:

    1. 初始化字符串: 从一个已知的神奇字符串开始,例如 "122"。这是因为我们可以确定前几个字符的生成规则。
    2. 构建字符串: 使用两个指针 ij,其中 i 指向当前需要生成的字符的位置,j 指向当前用于生成字符的规则的位置。
    3. 生成字符:
      • 根据 s[j] 的值确定接下来要添加多少个字符。
      • 如果 s[j]'1',则添加一个与 s[i-1] 不同的字符。
      • 如果 s[j]'2',则添加两个与 s[i-1] 不同的字符。
    4. 计数 1 的个数: 在生成字符串的过程中,统计前 n 个字符中 '1' 的个数。

    代码实现:

    class Solution: def magicalString(self, n: int) -> int: if n == 0: return 0 if n <= 3: return 1

        # 初始化字符串
        s = [1, 2, 2]
        i, j = 2, 2
        count_1 = 1  # 已经有一个 '1'
    
        while len(s) < n:
            if s[j] == 1:
                # 添加一个与当前不同的字符
                next_char = 3 - s[-1]
                s.append(next_char)
                if next_char == 1:
                    count_1 += 1
                i += 1
            elif s[j] == 2:
                # 添加两个与当前不同的字符
                next_char = 3 - s[-1]
                s.append(next_char)
                s.append(next_char)
                if next_char == 1:
                    count_1 += 2
                i += 2
            j += 1
    
        # 如果生成的字符串长度超过 n,需要修正 count_1
        if len(s) > n and s[n] == 1:
            count_1 -= 1
    
        return count_1

    示例

    sol = Solution() print(sol.magicalString(6)) # 输出:3

    解释:

    1. 初始化: s = [1, 2, 2]i = 2j = 2count_1 = 1
    2. 生成字符串:
      • s[2] = 2,添加两个与 s[-1](即 2)不同的字符,即两个 1s 变为 [1, 2, 2, 1, 1]count_1 变为 3
      • j 增加 1,变为 3。
      • s[3] = 1,添加一个与 s[-1](即 1)不同的字符,即一个 2s 变为 [1, 2, 2, 1, 1, 2]
      • j 增加 1,变为 4。
    3. 终止条件:s 的长度达到 n 时停止生成。

    通过这种方式,我们可以有效地构建神奇字符串并统计前 n 个字符中 '1' 的个数。