哦,不!你不小心把一个长篇文章中的空格、标点都删掉了,并且大写也弄成了小写。像句子"I reset the computer. It still didn’t boot!"
已经变成了"iresetthecomputeritstilldidntboot"
。在处理标点符号和大小写之前,你得先把它断成词语。当然了,你有一本厚厚的词典dictionary
,不过,有些词没在词典里。假设文章用sentence
表示,设计一个算法,把文章断开,要求未识别的字符最少,返回未识别的字符数。
注意: 本题相对原题稍作改动,只需返回未识别的字符数
示例:
1 2 3 4 5 输入: 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 来记录结果,dp[i]表示句子中前 i 个字符中最少的未识别字符数。dp[0] = 0 代表空字符串时2没有未识别的字符。
对于句子中前 i 个字符,有两种情况:
可能由前面的 [0, j) 字符串加上一个单词构成;dp[i] = min(dp[i], dp[j]);
前 i - 1 个字符加上第 i 个字符 ,dp[i] = dp[i - 1] + 1。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 class Solution { public int respace (String[] dictionary, String sentence) { Set<String> dic = new HashSet<>(); for (String s : dictionary){ dic.add(s); } int n = sentence.length(); int [] dp = new int [n + 1 ]; for (int i = 1 ; i <= n; i++){ dp[i] = dp[i - 1 ] + 1 ; for (int j = 0 ; j < i; j++){ if (dic.contains(sentence.substring(j, i))){ dp[i] = Math.min(dp[i], dp[j]); } } } return dp[n]; } }
时间复杂度O(n^3),contains 方法时间复杂度O(1),substring 方法时间复杂度O(n)。空间复杂度O(n)。
时间复杂度较高的地方就是如何快速判断子串是否存在于词典中,这里使用「字典树 Trie」来进行优化,Trie 时是一种最大程度利用多个字符串前缀信息的数据结构,它可以在 O(w)时间复杂度内判断一个字符串是否是一个字符串集合中某个字符串的前缀,其中 w 代表字符串的长度。
我们将所有单词「反序」插入到字典树中,然后在每次判断子串末尾是否是一个单词时,从子串末尾i 开始遍历,同时在 Trie 上从根结点开始出发,当走到sentence[j] 在 Trie 上没有相应的位置,说明 sentence[j…i - 1]不是一个单词,退出循环。对于判断字典树结点是否为叶子结点,我们在单词末尾的结点上打上一个 isEnd 的标记。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 class Trie { public Trie[] next; public boolean isEnd; public Trie () { next = new Trie[26 ]; isEnd = false ; } public void insert (String s) { Trie curPos = this ; for (int i = s.length() - 1 ; i >= 0 ; i--){ int t = s.charAt(i) - 'a' ; if (curPos.next[t] == null ){ curPos.next[t] = new Trie(); } curPos = curPos.next[t]; } curPos.isEnd = true ; } } class Solution { public int respace (String[] dictionary, String sentence) { int n = sentence.length(); Trie root = new Trie(); for (String word : dictionary){ root.insert(word); } int [] dp = new int [n + 1 ]; Arrays.fill(dp, Integer.MAX_VALUE); dp[0 ] = 0 ; for (int i = 1 ; i <= n; i++){ dp[i] = dp[i - 1 ] + 1 ; Trie curPos = root; for (int j = i; j >= 1 ; j--){ int t = sentence.charAt(j - 1 ) - 'a' ; if (curPos.next[t] == null ){ break ; }else if (curPos.next[t].isEnd){ dp[i] = Math.min(dp[i], dp[j - 1 ]); } if (dp[i] == 0 ){ break ; } curPos = curPos.next[t]; } } return dp[n]; } }
时间复杂度O(n^2)。空间复杂度O(n)。