执行所有后缀指令

标签: 字符串 模拟

难度: Medium

现有一个 n x n 大小的网格,左上角单元格坐标 (0, 0) ,右下角单元格坐标 (n - 1, n - 1) 。给你整数 n 和一个整数数组 startPos ,其中 startPos = [startrow, startcol] 表示机器人最开始在坐标为 (startrow, startcol) 的单元格上。

另给你一个长度为 m 、下标从 0 开始的字符串 s ,其中 s[i] 是对机器人的第 i 条指令:'L'(向左移动),'R'(向右移动),'U'(向上移动)和 'D'(向下移动)。

机器人可以从 s 中的任一第 i 条指令开始执行。它将会逐条执行指令直到 s 的末尾,但在满足下述条件之一时,机器人将会停止:

  • 下一条指令将会导致机器人移动到网格外。
  • 没有指令可以执行。

返回一个长度为 m 的数组 answer ,其中 answer[i] 是机器人从第 i 条指令 开始 ,可以执行的 指令数目

示例 1:

输入:n = 3, startPos = [0,1], s = "RRDDLU"
输出:[1,5,4,3,1,0]
解释:机器人从 startPos 出发,并从第 i 条指令开始执行:
- 0: "RRDDLU" 在移动到网格外之前,只能执行一条 "R" 指令。
- 1:  "RDDLU" 可以执行全部五条指令,机器人仍在网格内,最终到达 (0, 0) 。
- 2:   "DDLU" 可以执行全部四条指令,机器人仍在网格内,最终到达 (0, 0) 。
- 3:    "DLU" 可以执行全部三条指令,机器人仍在网格内,最终到达 (0, 0) 。
- 4:     "LU" 在移动到网格外之前,只能执行一条 "L" 指令。
- 5:      "U" 如果向上移动,将会移动到网格外。

示例 2:

输入:n = 2, startPos = [1,1], s = "LURD"
输出:[4,1,0,0]
解释:
- 0: "LURD"
- 1:  "URD"
- 2:   "RD"
- 3:    "D"

示例 3:

输入:n = 1, startPos = [0,0], s = "LRUD"
输出:[0,0,0,0]
解释:无论机器人从哪条指令开始执行,都会移动到网格外。

提示:

  • m == s.length
  • 1 <= n, m <= 500
  • startPos.length == 2
  • 0 <= startrow, startcol < n
  • s'L''R''U''D' 组成

Submission

运行时间: 64 ms

内存: 16.1 MB

class Solution:
    def executeInstructions(self, n: int, startPos: List[int], s: str) -> List[int]:
        d_x,d_y,len_s = {},{},len(s)
        y,x = startPos
        log,ans = [],[0]*len_s
        #得到最新坐标
        for i,move in enumerate(s):
            x += {"L":-1,"U":0,"D":0,"R":1}[move]
            y += {"L":0,"U":-1,"D":1,"R":0}[move]
        #第二次遍历反向求答案
        for i,move in enumerate(s[::-1],1):
            d_x[x],d_y[y] = i,i
            x += {"L":1,"U":0,"D":0,"R":-1}[move]
            y += {"L":0,"U":1,"D":-1,"R":0}[move]
            #x与y是走到第n-i个时的坐标,d_x[x坐标],d_y[y坐标]的值为最新到达x/y坐标的命令计数(即执行第n-i个命令时)
            a = i
            x1,y1 = x-startPos[1],y-startPos[0]
            if -1+x1 in d_x:
                a = min(a,i-d_x[-1+x1])
            if n+x1 in d_x:
                a = min(a,i-d_x[n+x1])
            if -1+y1 in d_y:
                a = min(a,i-d_y[-1+y1])
            if n+y1 in d_y:
                a = min(a,i-d_y[n+y1])
            ans[len_s-i] = a
        return ans

Explain

题解首先遍历一次指令字符串s,计算出机器人每执行一步后的坐标。然后再反向遍历字符串s,同时更新两个字典d_x和d_y,这两个字典用来记录机器人到达某个x或y坐标的最新指令计数。在反向遍历过程中,通过比较当前位置与边界的关系,使用这两个字典来确定从当前位置开始,机器人能够执行的最大指令数。对于每个起始指令位置,计算出从该位置开始直到结束或出界的可执行指令数,并存储在结果数组ans中。

时间复杂度: O(m)

空间复杂度: O(m)


class Solution:
    def executeInstructions(self, n: int, startPos: List[int], s: str) -> List[int]:
        d_x,d_y,len_s = {},{},len(s)
        y,x = startPos
        log,ans = [],[0]*len_s
        # 第一次正向遍历指令,计算机器人每步后的坐标
        for i,move in enumerate(s):
            x += {'L':-1,'R':1,'U':0,'D':0}[move]
            y += {'L':0,'R':0,'U':-1,'D':1}[move]
        # 第二次反向遍历,计算从每个位置开始能执行的最大指令数
        for i,move in enumerate(s[::-1], 1):
            d_x[x],d_y[y] = i,i
            x += {'L':1,'R':-1,'U':0,'D':0}[move]
            y += {'L':0,'R':0,'U':1,'D':-1}[move]
            a = i  # 从当前位置开始的最大指令数
            x1,y1 = x-startPos[1],y-startPos[0]
            # 检查并更新机器人是否会移出边界
            if -1+x1 in d_x:
                a = min(a, i-d_x[-1+x1])
            if n+x1 in d_x:
                a = min(a, i-d_x[n+x1])
            if -1+y1 in d_y:
                a = min(a, i-d_y[-1+y1])
            if n+y1 in d_y:
                a = min(a, i-d_y[n+y1])
            ans[len_s-i] = a  # 存储结果
        return ans
    

Explore

在反向遍历过程中,使用`d_x`和`d_y`字典记录机器人到达每个x和y坐标的最新指令计数的原因是为了快速确定机器人从当前位置起能够继续移动的最大步数,直到它可能移出边界。这样做可以有效地避免重复计算同一位置的最大可移动步数,提高算法效率。当机器人再次访问同一个x或y坐标时,可以直接使用字典中存储的最新指令计数来判断从该位置开始的最大可执行步数,从而减少不必要的计算。

在本题中,每个x和y坐标值的范围是确定的,从`0`到`n-1`,因此不会出现多个不同的坐标映射到相同键的情况。每次机器人到达一个新的x或y坐标,字典`d_x`或`d_y`会更新或添加该坐标对应的指令计数。由于是反向遍历,每个坐标的最新指令计数总是最先被记录,确保了字典的准确性和数据不会被错误覆盖。

在题解中,通过检查机器人每次移动后的坐标是否超出了网格的边界来处理机器人可能移出边界的情况。具体实现中,用`d_x`和`d_y`字典存储每个x和y坐标的最新指令计数,并在每次移动后检查该坐标是否已经存在于字典中。如果存在,比较当前步数与字典中存储的步数,计算出从当前位置开始到可能移出边界的最大指令数。通过这种方式,算法能够有效地判断并计算机器人在遇到边界时的停止位置。

第二次反向遍历通过从字符串的末尾开始,逐步向字符串开头遍历,可以确保每个位置的最大指令数是准确计算的。这种方式利用了动态规划的思想,即从已知的结果(边界或先前计算的结果)推导出当前问题的解。通过记录每个坐标点的最新指令计数,并结合边界检查,可以确保每次计算都是基于当前最可能的最大可执行步数。这样的逆序遍历确保了每个起始位置的指令计数都是在考虑所有后续可能性的基础上得出的,从而保证了计算结果的准确性。