面试题 17.13. 恢复空格
Title
哦,不!你不小心把一個長篇文章中的空格、標點都刪掉了,并且大寫也弄成了小寫。像句子"I reset the computer. It still didn’t boot!“已經變成了"iresetthecomputeritstilldidntboot”。在處理標點符號和大小寫之前,你得先把它斷成詞語。當然了,你有一本厚厚的詞典dictionary,不過,有些詞沒在詞典里。假設文章用sentence表示,設計一個算法,把文章斷開,要求未識別的字符最少,返回未識別的字符數。
示例:
輸入:
dictionary = [“looked”,“just”,“like”,“her”,“brother”]
sentence = “jesslookedjustliketimherbrother”
輸出: 7
解釋: 斷句后為"jess looked just like tim her brother",共7個未識別字符。
提示:
0 <= len(sentence) <= 1000
dictionary中總字符數不超過 150000。
你可以認為dictionary和sentence中只包含小寫字母。
Trie + 動態規劃
Solve
定義 dp[i] 表示考慮前 i 個字符最少的未識別的字符數量,從前往后計算 dp 值。
考慮轉移方程,每次轉移的時候我們考慮第 j(j≤i) 個到第 i 個字符組成的子串 sentence[j?1?i?1] (注意字符串下標從 0 開始)是否能在詞典中找到,如果能找到的話按照定義轉移方程即為
dp[i]=min(dp[i],dp[j?1])dp[i]=min(dp[i],dp[j?1])dp[i]=min(dp[i],dp[j?1])
否則沒有找到的話我們可以復用 dp[i?1] 的狀態再加上當前未被識別的第 i 個字符,因此此時 dp 值為
dp[i]=dp[i?1]+1dp[i]=dp[i?1]+1dp[i]=dp[i?1]+1
最后問題化簡成了轉移的時候如何快速判斷當前子串是否存在于詞典中,可以通過字典樹 Trie 來優化查找。
Trie 是一種最大程度利用多個字符串前綴信息的數據結構,它可以在 O(w) 的時間復雜度內判斷一個字符串是否是一個字符串集合中某個字符串的前綴,其中 w 代表字符串的長度。
我們將詞典中所有的單詞「反序」插入字典樹中,然后每次轉移的時候我們從當前的下標 i 出發倒序遍歷 i?1,i?2,?,0。在 Trie 上從根節點出發開始走,直到走到當前的字符 sentence[j] 在 Trie 上沒有相應的位置,說明 sentence[j?i?1] 不存在在詞典中,且它已經不是「任意一個單詞的后綴」,此時我們直接跳出循環即可。否則,我們需要判斷當前的子串是否是一個單詞,這里我們直接在插入 Trie 的時候在單詞末尾的節點打上一個 isEnd 的標記即可,這樣我們在走到某個節點的時候就可以判斷是否是一個單詞的末尾并根據狀態轉移方程更新我們的 dp 值。
具體實現以及示例的圖畫解析可以看下面:
Code
動態規劃
def respace(self, dictionary: List[str], sentence: str) -> int:n = len(sentence)dp = [i for i in range(n + 1)]for i in range(n):for j in range(i, -1, -1):if sentence[j:i + 1] in dictionary:dp[i + 1] = min(dp[i + 1], dp[j])else:dp[i + 1] = min(dp[i + 1], dp[i] + 1)return dp[-1]動態規劃+字典樹
class Solution:def respace(self, dictionary, sentence: str) -> int:trie = Trie()# 初始化字典樹for word in dictionary:cur = triefor i in range(len(word)-1, -1, -1):cur = cur.next_nodes[word[i]]cur.isEnd = Truedp = [float('inf') for _ in range(len(sentence)+1)]dp[0] = 0for i in range(1, len(sentence)+1):dp[i] = dp[i - 1] + 1j = icur = triewhile j >= 1 and sentence[j-1] in cur.next_nodes:cur = cur.next_nodes[sentence[j]]if cur.isEnd == True:dp[i] = min(dp[i], dp[j-1])if dp[i] == 0:breakj -= 1return dp[-1]class Trie:def __init__(self):self.next_nodes = collections.defaultdict(Trie)self.isEnd = False 與50位技術專家面對面20年技術見證,附贈技術全景圖總結
以上是生活随笔為你收集整理的面试题 17.13. 恢复空格的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Vue CLI:全局CLI配置
- 下一篇: Uncaught TypeError: