emmmmmmm这个题目很难,,,需要用到Trie字典树,以及动态规划相关知识。算是很重要的一个题目了。手动标记一下23333。
------------------------------------------------------------------
题目:
哦,不!你不小心把一个长篇文章中的空格、标点都删掉了,并且大写也弄成了小写。像句子"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中只包含小写字母。
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/re-space-lcci
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
--------------------------------------------------------------------------------------------------------------
思路:刚刚看到题目的时候,,,以为就是一个单纯的字符串匹配问题,然后就发现大错特错。。。
RETRY:动态规划是肯定的,不然暴力法必定达到O(n^3)而超时。。。
至于字典树,也是临时补充了知识点。这里推荐参考:
https://blog.youkuaiyun.com/johnny901114/article/details/80711441
这位博主的讲解非常详细了:字典树以单个字符作为树节点,并且使用标记位记录从根到当前字符串是否是一个保存到字典树内的单词(因为字符串还可能是单词的前缀)。
使用字典树可以使得查询一个字符串的复杂度大大降低,并且字典树的效率比哈希表还高。。。
通过借助字典树这种数据结构,从而可以降低复杂度到O(n^2),经过试验可以顺利AC。
动态规划思路:
首先定义:dp[i]:表示前i个字符中,最少的未识别的字符数。
根据定义可以初始化:
①dp[0] = 0。这个很明显。
②dp数组的下标范围为0 ~ 给定无空格字符串长度(这里以len变量表示)。
状态转移方程:
subString = sentence.subString(j-1, i)。因为字符串中下标从0开始,所有使用 j-1。
如果 subString是保存在字典树中的一个单词:
dp[i] = min(dp[i], dp[j-1]) //这里的j-1与上面的j-1含义不一样。注意。
如果 subString不是:
dp[i] = dp[i-1] + 1 //很明显了,不再说明。。。
经过这样的设计以及动态规划的思路,使得整体的时间复杂度在O(n^2)左右,顺利AC。
--------------------------------------------------------------
实现代码:
JAVA版:
package 面试题;
/*
name: _17_13_important
user: ly
Date: 2020/7/9
Time: 7:31
*/
public class _17_13_important {
public static void main(String []args){
String []dic = {"looked","just","like","her","brother"};
String sentence = "jesslookedjustliketimherbrother";
System.out.println(new _17_13_important().respace(dic, sentence));
}
public int respace(String[] dictionary, String sentence) {
int ans = 0;
Trie trie = new Trie();
//加入字典树中
for(String dic : dictionary){
trie.add(dic);
}
int len = sentence.length();
int []dp = new int[len+1]; //dp[i] :表示前i个字符中为识别的最少字符数
for(int i = 0;i < len+1;i++) dp[i] = Integer.MAX_VALUE;
dp[0] = 0; //前0个字符自然是0
// 如果子字符串 [j-i]匹配了,那么dp[i] = min(dp[i], dp[j-1])。此时前i个字符中未识别字符数与前j个字符中未识别字符数有关
// 如果不匹配,那么:dp[i] = dp[i-1] + 1。即前i个字符中未识别的字符数与前i-1个字符中未识别的字符数有关
// 所有很明显,dp[i]中的i应该取0-len之间,两边都闭。
// j则取1到i之间,同样两边都闭。
for(int i = 1;i <= len;i++){
dp[i] = dp[i-1] + 1;
for(int j = 1;j <= i;j++){
String sub = sentence.substring(j-1, i);
if(trie.contains(sub)){
System.out.println("matched:"+sub);
dp[i] = Math.min(dp[i], dp[j-1]);
}
if(dp[i] == 0) break;
}
}
return dp[len];
// System.out.println(sans);
// return sentence.length() - sans.replaceAll(" ","").length();
}
}
-------------------------------------------------------------------
心得:
进一步加深了动态规划是思想应用。
get了新的数据结构。