恢复空格
题目描述
哦,不!你不小心把一个长篇文章中的空格、标点都删掉了,并且大写也弄成了小写。像句子"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个未识别字符。
动态规划+字符串哈希
我们可以使用dp[i]来存储sentence中自0到第i个字符的最少未识别字符的数量。若第i个字符未识别,那么dp[i]=dp[i-1]+1
.若是第i个字符串可以识别,那么继续讨论第i个字符可以和之前的几个字符串组合起来,并使得未识别字符数最小。若第i个字符自己就算一个单词,那么dp[i]=dp[i-1],若第i个字符和第i-1个字符组成一个单词,则dp[i]=min(dp[i-1],dp[i-2])….直到查看完第i个字符是否可以和前面的所有字符组合成一个单词。为此我们还需要在dp数组中设置一个边界。dp[0]用来表示前0个字符的最少未匹配字符数,当然它的值为0.即dp[0]=0
.
接下来的问题是如何判断第i个字符可以和之前的字符组成一个单词呢,从直觉来说,暴力法可以完成这个功能,但是时间复杂度一定很高,字符串一旦长了,必定会花费很多的时间。有一个办法可以解决,那就是求字符串哈希。Rabin-Karp 字符串编码是一种将字符串映射成整数的编码方式,可以看成是一种哈希算法。假设字符串包含的字符种类不超过 ∣Σ∣(其中Σ 表示字符集),那么我们选一个大于等于|Σ∣ 的整数base,就可以将字符串看成 base 进制的整数,将其转换成十进制数后,就得到了字符串对应的编码。建议先了解字符串哈希,基础教程转载自CSDN作者pengwill97。
对于字典中的单词,求出其字符串哈希,然后用hash表储存起来。那么,对于第i个字符串是否能与之前的字符组成单词就可以在O(1)的时间内得出结果。
class Solution {
public static final int BASE = 29;
public static final int MOD = Integer.MAX_VALUE;
public static int respace(String[] dictionary, String sentence) {
Set<Long> set = new HashSet<>(dictionary.length);
for (String s : dictionary) {
set.add(getHash(s));
}
//dp[i]储存了sentence从0到下标i之间最小未识别字符数
int[] dp = new int[sentence.length() + 1];
for (int i = 1; i <= sentence.length(); i++) {
dp[i] = dp[i - 1] + 1;//假设第i个字符不会被识别
long hash = 0;
for (int j = i; j > 0; j--) {//从后向前
int currentHash = sentence.charAt(j-1) - 'a' + 1;
hash = ((hash * BASE) + currentHash) % MOD;
if (set.contains(hash)) {//字典中存在sentence[j:i]这一单词
dp[i] = Math.min(dp[j-1], dp[i]);//在识别当前字符与不识别当前字符的两种情况中取较小值
}
}
}
return dp[dp.length - 1];
}
public static long getHash(String s) {
//从后向前遍历字符串计算字符串哈希,以便于从后向前dp
long hash = 0;
for (int i = s.length()-1; i >=0; i--) {
hash = (hash * BASE + s.charAt(i) - 'a' + 1) % MOD;
}
return hash;
}
}
在以上代码中,因为需要依次判断第i个字符与第i-1、i-2、…、0个字符组成字符串,所以字符串哈希采用的是从后向前计算哈希。相当于将字符串倒了一头。