leetcode 面试题 17.13. 恢复空格(dp)

题目描述:
哦,不!你不小心把一个长篇文章中的空格、标点都删掉了,并且大写也弄成了小写。像句子"I reset the computer. It still didn’t boot!“已经变成了"iresetthecomputeritstilldidntboot”。在处理标点符号和大小写之前,你得先把它断成词语。当然了,你有一本厚厚的词典dictionary,不过,有些词没在词典里。假设文章用sentence表示,设计一个算法,把文章断开,要求未识别的字符最少,返回未识别的字符数。
注意:本题相对原题稍作改动,只需返回未识别的字符数

思路:一开始没看出是dp,以为是模拟。。然后就签到失败了。。
首先是dp式子:dp[j]表示给出sentence的(0,j)串的最大匹配数
我的思路是求最少未识别符号可以转换为求最多识别的字符数(虽然不转换比较直接),写出状态转移方程:(i >=1 j >=1)
dp[j] = max(dp[j] , dp[j - 1] (把之前子串的最大匹配数传过来)
dp[j] = max(dp[i] + j - i + 1 , dp[j ] ) (sentence的子串(i,j)匹配到了字典中的单词)(可以选择该单词或者不选)

然后是trie树,对sentence每一个位置都是用tire树进行匹配即可,我这里使用正向匹配,官方题解是逆向匹配,但是其实都一样。虽然正向写代码似乎饶了一点

时间复杂度为O(n*n + m),n为sentence的长度,m为字典的字符量(为建树的时间)

c++代码:

struct node {
    int child[26] = {0};
    bool yz = 0;   //true表示是一个单词末尾
};

class Solution {
public:
	const static int maxn = 2e5;
    node tire[maxn];
    int dp[2000] , cnt = 0;

    int insert(string &s) {
    	int u = 0;
        for(int j = 0 ; j < s.length() ; j++) {
            int t = s[j] - 'a';
            if(!tire[u].child[t]) {
                tire[u].child[t] = cnt + 1;
                cnt++;
            }
            u = tire[u].child[t];
        }
        tire[u].yz = 1;
        return u;
	} 

    int respace(vector<string>& dictionary, string sentence) {
    	int u = 0 , len = sentence.length();
		for(int i = 0 ; i < dictionary.size() ; i++) {
    		u = insert(dictionary[i]);
		}
		for(int i = 0 ; i < len ; i++) {
			u = 0;
			for(int j = i ; u && j < len; j++) {
				int t = sentence[j] - 'a';
				u = tire[u].child[t];
				if(!u)break;
				if(tire[u].yz) {
					dp[j + 1] = max(dp[i] + j - i + 1 , dp[j + 1]);
				}
			}
			dp[i + 1] = max(dp[i + 1] , dp[i]);
		}
		return len - dp[len - 1]; 
	}
};

猜你喜欢

转载自blog.csdn.net/qq_39475280/article/details/107248439