算法题:恢复空格
Contents
本文详细解释《程序员面试金典》中的《恢复空格》一题。
题目描述
哦,不!你不小心把一个长篇文章中的空格、标点都删掉了,并且大写也弄成了小写。像句子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 中只包含小写字母。
分析
我们可以使用动态规划来解决这道题,dp[i] 表示在以索引 i 结尾的字符串中,它之前最多能匹配单词的数量。
需要注意的是,最后需要返回的是未识别的字符数,也就是没有匹配成功的字符数。因此,我们用字符串的总长度减去已经匹配完成的字符串长度,那么最后剩下的就是未识别的字符数。
如下所示:
- 刚开始来到索引为 0 的
L
位置,由于L
不在字典中,因此,dp[0]=0。 - 来到索引为 1 的
e
位置,由于字符串e
和Le
都不在字典中,因此,dp[1]=0。 - 来到索引为 2 的
e
位置,由于字符串Lee
、ee
、e
都不在字典中,因此,dp[2]=0。 - 来到索引为 3 的
t
位置,由于字符串Leet
在字典中,并且还是最长的,因此,dp[3]=4。
接下来是索引为 4 的C
位置,在以C
结尾的字符串中,只有除了Leet
可以从字典中匹配到以外,etc
也是可以匹配到的。因此,dp[4]=3+dp[1]=3,其中,这里的 3 指的是etc
字符串的长度,后面的 dp[1] 指的是以e
结尾所能最多匹配单词的数量。虽然,我们看到 dp[4] 在当前是等于 3 的,但是仍然不能取,我们还应该考虑到字符串Leet
,它的长度是 4,虽然不是以c
结尾的,但是它出现在了字典中,并且当前的长度最长。所以,我们在这里需要取最大值,即 dp[4]=Math.max(3+dp[1], 4)。如下所示:
接下来是索引为 5 的o
位置以及索引为 6 的d
,它们的取值只能取到最大值 4。如下所示:
接下来是索引为 7 的e
,此时code
可以在字典中进行匹配。此时,dp[7]=Math.max(4, 4+dp[7-4])=8。其中,前面的 4 表示前一个位置所能表示的最大长度,而 4+dp[7-4] 表示以e
结尾的、在字典中能够匹配的字符串code
的长度为 4,e
的当前位置是 7,然后将去code
的长度,就是 dp[7-4]。如下图所示:
接下来是索引为 8 的g
(题目说明已经忽略大小写),很容易看出,当前 dp[8] 的值依赖于前面的值,也就是 8。如下所示:
接下来是索引为 9 的o
,由于go
可以在字典中找到,因此,2+dp[9-2] 表示go
的字符串长度为 2,在加上 dp[7] 位置上的数,然后再与o
的前一位(也就是 8)取最大值,最终 dp[9] 就等于 10。如下图所示:
接下来是索引为 10 的o
以及索引为 11 的d
,它们都不在字典中,因此,都为 10。如下所示:
最后,12(字符串长度) 减 10(已经匹配的字符串长度) 得 2(没有匹配的字符串长度)。因此,最终返回 2 即可。
代码
参见:https://github.com/dyfloveslife/LeetCodeAndSwordToOffer/blob/master/src/LeetCodeSolution/AlgorithmThought/_02_DP/_17_13_ReSpaceLCCI/Solution.java