哦,不!你不小心把一个长篇文章中的空格、标点都删掉了,并且大写也弄成了小写。像句子"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个字符未识别的字符最少数,那么当s[j-1:i)在字典中时,dp[i]=min(dp[i],dp[j-1]),否则dp[i]=dp[i-1]+1;整体和单词拆分类似,双重循环+判断即可。
class Solution{
public:
int respace(vector<string>& dictionary,string sentence){
int len=sentence.size();
unordered_set<string>set(dictionary.begin(),dictionary.end());
vector<int>dp(len+1,0);
for(int i=1;i<=len;++i){
dp[i]=dp[i-1]+1;
for(int j=i;j>=1;--j){
if(set.find(sentence.substr(j-1,i-j+1))!=set.end()){
dp[i]=min(dp[i],dp[j-1]);
}
}
}
return dp[len];
}
};
同样,这种方式造成了重复计算,当以某后缀为字符串不存在字典中时,那么以该后缀为字符串的应该都不在字典中,这种情况应该跳过。如dic={“qw”},vec={“bc”,“ac”},像这种vec中的字符串都无法匹配字典,因为它们的后缀是’c’,而该字符不在字典中。使用字典树跳过这种情况,代码如下:
class Trie_node{
public:
Trie_node* next[26]={nullptr};
bool isEnd;
Trie_node(){
isEnd=false;
}
void insert(const string& s){
Trie_node* curPos=this;
for(int i=s.size()-1;i>=0;--i){
int pos=s[i]-'a';
if(curPos->next[pos]==nullptr){
curPos->next[pos]=new Trie_node();
}
curPos=curPos->next[pos];
}
curPos->isEnd=true;
}
};
class Solution {
public:
int respace(vector<string>& dictionary, string sentence) {
int len=sentence.size();
Trie_node* root=new Trie_node();
for(const string& s:dictionary){
root->insert(s);
}
vector<int>dp(len+1,0);
for(int i=1;i<=len;++i){
dp[i]=dp[i-1]+1;
Trie_node* curPos=root;
for(int j=i;j>=1;--j){
int pos=sentence[j-1]-'a';
if(curPos->next[pos]==nullptr){
break;
}
else if(curPos->next[pos]->isEnd){
dp[i]=min(dp[i],dp[j-1]);
}
curPos=curPos->next[pos];
}
}
return dp[len];
}
};