↓快轉到主要內容
  1. LeetCode/

LeetCode 1320: Minimum Distance to Type a Word Using Two Fingers

·1 分鐘· ·
LeetCode Hard Dynamic-Programming State-Machine String
Wei Yi Chung
作者
Wei Yi Chung
Working at the contributing of open source, distributed systems, and data engineering.
目錄

基本資料
#

難易度: hard 第一次嘗試:2026-08-25

  • 提交紀錄:Accepted ×2(三維 DP → 一維 DP)

解題思路
#

鍵盤是 6 欄的格子,字母 c 的位置是 (c // 6, c % 6),距離是曼哈頓距離。手指一開始可以放在任何地方,第一次移動不算距離,用一個特別的位置 26 代表「還沒用過」,從 26 出發的距離是 0。

最直接的狀態是 dp[i][f1][f2](打完第 i 個字母,兩根手指的位置),27 × 27 × n。

觀察:打完 word[i] 時,一定有一根手指正在 word[i] 上。所以只要記另一根手指在哪:dp[other]。打 word[i] 時(上一個字母是 prev = word[i - 1]):

  • 用剛剛打 prev 的那根手指:另一根手指不動,dp'[other] = dp[other] + dist(prev, cur)。
  • 用另一根手指:它從 other 移到 cur,原本在 prev 上的那根變成「另一根」,dp'[prev] = dp[other] + dist(other, cur)。

我的解法
#

class Solution:
    def minimumDistance(self, word: str) -> int:
        def dist(a: int, b: int) -> int:
            if a == 26:
                return 0                           # 還沒用過的手指,第一次移動不算
            return abs(a // 6 - b // 6) + abs(a % 6 - b % 6)

        INF = float("inf")
        dp = [INF] * 27                            # dp[other]:另一根手指在 other
        dp[26] = 0
        for i in range(1, len(word)):
            prev, cur = ord(word[i - 1]) - 65, ord(word[i]) - 65
            ndp = [INF] * 27
            for other in range(27):
                if dp[other] == INF:
                    continue
                ndp[other] = min(ndp[other], dp[other] + dist(prev, cur))   # 同一根手指
                ndp[prev] = min(ndp[prev], dp[other] + dist(other, cur))    # 換另一根
            dp = ndp
        return min(dp)

我第一次 AC 是三維的 dp[i][f1][f2],跑了 277ms;觀察到「一定有一根手指在上一個字母上」之後壓成一維,50ms。

複雜度
#

時間 O(27 · n),空間 O(27)。

要特別避免的錯誤
#

  • 手指的初始位置不算距離:用一個「還沒用過」的狀態處理。
  • 換手指之後,原本打 prev 的那根變成「另一根」,狀態是 prev。

面試口說整理
#

打完一個字母時一定有一根手指在它上面,所以狀態只要記另一根手指的位置,用 26 代表還沒用過。打下一個字母時,可以用剛剛那根手指移過去,另一根不動;或用另一根移過去,這時剛剛那根變成另一根。兩種轉移取最小,時間 O(27n)。

相關文章

LeetCode 902: Numbers At Most N Given Digit Set
·1 分鐘
LeetCode Hard Dynamic-Programming Digit-Dp Math Binary-Search String
LeetCode 902 解題筆記:數位 DP,狀態是 (pos, tight, started);還沒開始時可以跳過這一位,代表位數比 n 少的數
LeetCode 552: Student Attendance Record II
·2 分鐘
LeetCode Hard Dynamic-Programming State-Machine
LeetCode 552 解題筆記:狀態機 DP,狀態是「A 用了幾次 × 結尾連續幾個 L」共 6 種;每天選 P、A、L 決定下一個狀態
LeetCode 1147: Longest Chunked Palindrome Decomposition
·1 分鐘
LeetCode Hard Dynamic-Programming Greedy Two-Pointers Rolling-Hash String
LeetCode 1147 解題筆記:從兩端同時往內累積字串,左右一相等就切成一對;越早切越好,剩下的中間(如果有)算一塊
LeetCode 3952: Maximum Total Value of Covered Indices
·1 分鐘
LeetCode Medium Dynamic-Programming State-Machine Greedy String
LeetCode 3952 解題筆記:每個代幣只能往左移一格,狀態是「這格的代幣有沒有往左移」;往左移只有在左邊那格原本沒被蓋住時才有用
LeetCode 2912: Number of Ways to Reach Destination in the Grid
·2 分鐘
LeetCode Hard Dynamic-Programming State-Machine Math Combinatorics
LeetCode 2912 解題筆記:格子太多,但相對終點只分四類:終點、同一列、同一行、都不同;每一類的總方法數一起轉移,O(k)
LeetCode 488: Zuma Game
·2 分鐘
LeetCode Hard Dynamic-Programming Memoization Breadth-First-Search String Stack
LeetCode 488 解題筆記:board 最多 16 顆、hand 最多 5 顆,把 (board, hand) 整個當狀態做 DFS + 記憶化;只在「同色球前面」和「拆開兩顆同色球」兩種位置插入