LeetCode面试题 17.13 回复空格

  • 题意:
    给定一个字典,里面包含一些string,给定一个句子,从中匹配最多的字典中的string。输出未匹配的最少的字符数。
  • 思路:
    动态规划 + 字典树
    (1)动态规划
    dp[i] 代表 前i个字符未匹配的最少字符数。
    当遍历到第i个字符时,判断从 j 到第i个字符是否在字典中,如果在字典中,则
    dp[i] = min(dp[i], dp[j-1])。为什么是 dp[j-1],因为第j个字符到第i个都在字典中,所以是前 j-1个字符的未匹配最少字符数。
    若没在字典中匹配,则 dp[i] = dp[i-1] + 1。即相对于前 i-1个字符,多了一个第i个字符没有匹配。
    (2)字典树 Trie
    如何快速判断当前子串是否在字典中,就要用到字典树。一般的字典树,也称为前缀树。
    来看一个例子,用单词 love、meet、pain、like构建字典树 Trie:
    在这里插入图片描述
    可以看到,根节点不保存字符;根节点到叶子节点的路径就是一个字符串;有公共前缀的字符串like 和love 在一个子树中。
    这样每次寻找一个字符时,有公共前缀的字符便很容易寻找。
    而在此题中,因为我们遍历i节点,然后从j ——i判断是否有字符匹配。那么我们相当于建立的是一个后缀树。上图中的树就会变成:
    在这里插入图片描述
    例如我们目标串为asdflike.
    当我们遍历到e时,j from i to 0。e在树中,j --;否则只要匹配不到叶子节点就退出循环。这样当 匹配到 l 时,我们创建树时预先在每个叶子节点设置一个isEnd标志,标志是叶子节点,代表我们匹配了一个字符串。
  • 代码:
class Trie{
    
    
public:
    Trie* next[26] = {
    
    nullptr};
    bool isEnd;
    Trie(){
    
    
        isEnd = false;
    }
    void insert(string s){
    
    
        Trie* curPos = this;
        for(int i = s.length()-1;i >= 0;i--){
    
    
            int tmp = s[i] - 'a';
            if(curPos->next[tmp] == nullptr){
    
    
                curPos->next[tmp] = new Trie();
            }
            curPos = curPos->next[tmp];
        }
        curPos->isEnd = true;
    }
};
class Solution {
    
    
public:
    int respace(vector<string>& dictionary, string sentence) {
    
    
        int n = sentence.length();
        Trie* root = new Trie();
        for(auto& word : dictionary){
    
    
            root->insert(word);
        }
        vector<int> dp(n+1, INT_MAX);
        dp[0] = 0;
        for(int i = 1;i <= n;i++){
    
    
            dp[i] = dp[i-1] + 1;
            Trie* curPos = root;
            for(int j = i;j >= 1;j--){
    
    
                int t = sentence[j-1] - 'a';
                if(curPos->next[t] == nullptr){
    
    
                    break;
                }else if(curPos->next[t]->isEnd){
    
    
                    dp[i] = min(dp[i], dp[j-1]);
                }
                if(dp[i] == 0){
    
    
                    break;
                }
                curPos = curPos->next[t];
            }
        }
        return dp[n];
    }
};
  • 收获
    因为之前没怎么做过字符串dp,以及字典树的应用。这次学到了很多。当然肯定每个题都不一样,需要自己灵活运用。dp也需要多做题才能对状态转移公式有所掌握。

おすすめ

転載: blog.csdn.net/qq_39763472/article/details/107239652