Java刷题笔记9:恢复空格

题目描述

哦,不!你不小心把一个长篇文章中的空格、标点都删掉了,并且大写也弄成了小写。像句子"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个字符组成字符串,所以字符串哈希采用的是从后向前计算哈希。相当于将字符串倒了一头。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值