题目
哦,不!你不小心把一个长篇文章中的空格、标点都删掉了,并且大写也弄成了小写。像句子"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个未识别字符。
解法
用到了字典树(用来判断前缀、后缀,在这里比hash表速度更快):
1.如果头节点的next对应的字符未创建,创建空间,节点往下走
2.等到整个字符输入完成后,标记下已结束
此题用来判断后缀,所以倒着入站,looked—>dekool
root节点
-> [d,0]->[e,0]->[k,0]->[e,0]->[d,1]
-> [t,0]->[s,0]->[u,0]->[j,1]
-> [e,0]->[k,0]->[i,0]->[l,1]
-> [r,0]->[e,0]->[h,1]->[t,0]->[o,0]->[r,0]->[b,1]
最后字典树就变了这样,其中红色节点代表字符结束
dp[i]:表示在在下标i之前的有几个未识别的字符
- dp[i]=dp[i-1]+1
- 从i-1开始往前找, j = i-1;j >=0;i–
- 如果这个字符在字典树中不空,且s[j-1,j…i-1]的字符出现在字典中,即出现标记为1,
- 如果这个字符在字典树中是空的,退出
- 更新dp[i] = min(d[i],dp[j]),但是仍然往前一直匹配
这里给出几组测试数据和具体dp的值:
dictionary = ["looked","just","like","her","brother"]
sentence= jess looked just like tim her brother
0123 456789 4567 4567 456 789 78910111213 7
dictionary= ["aaa"]
sentence= aaa aaa
012 012 0
代码
#include <stdio.h>
#include <vector>
#include <cstring>
#include <iostream>
using namespace std;
class Trie {
public:
Trie* next[26] = {nullptr};//总共有26个字母,初始化为空
bool isend;
Trie(){
isend = false;//初始化 该节点不是字符串的结尾
}
void insert(string s){
Trie* tmp= this;
for(int i=s.length()-1;i>=0;i--)//由于从后往前找,这里需要倒着插入
{
int num = s[i]-'a';
if(tmp->next[num]==nullptr) //如果是新的字符出现,开辟空间,类似于树生根
{
tmp->next[num] = new Trie();
}
tmp = tmp->next[num];
}
tmp->isend = true; //整个字符串插入结束后,在末尾标记
}
};
class Solution {
public:
int respace(vector<string>& dictionary, string sentence) {
int n= sentence.length();
vector<int> dp (n+1,n);//开辟n+1个空间,vector初始化为n(每个字符都没有识别,最差的情况)
dp[0]=0;//初始化,解决数组从0开始问题
Trie* root = new Trie();
for (int i=0;i<dictionary.size();i++) //把字典中的字符串放入字典树中
root->insert(dictionary[i]);
for(int i=1;i<=n;i++)
{
dp[i]=dp[i-1]+1; //开辟n+1个空间,vector初始化为n(每个字符都没有识别,最差的情况)
Trie* tmp = root; //获取字典树头节点
for(int j=i-1;j>=0;j--) //倒着找字符串sentence[j:i]
{
int num = sentence[j]-'a';
if(tmp->next[num] == nullptr)
break;
else if(tmp->next[num]->isend==true)//如果一直往前找,dp[j,j+1...i-1]出现在字典中,dp[i]更新。
{
/*用min是为了找到更多匹配的字符,从而减少未识别的字符
例子:brother
0123456 4->0
一开始我们找到了her,但是最大匹配的是brother*/
dp[i] = min(dp[i],dp[j]);
}
tmp = tmp->next[num];
}
}
return dp[n];
}
};
int main()
{
vector<string> d(10);
d[0] = "looked";d[1]="just";d[3]="like";d[4]="her";d[5]="brother";
string st = "jesslookedjustliketimherbrother";
//d[0]="aaa";
//string st= "aaaaaa";
Solution s ;
cout<<s.respace(d, st);
return 0;
}
今天也是爱zz的一天哦!