面试题 17.13. 恢复空格-7月9日

题目

面试题 17.13. 恢复空格

我的思路

 这道题还挺有难度,没有做出来。

我最初的想法是在对sentence进行多次扫描,把所有在dictionary中存在的子串记录下来。符合条件的子串用两个整数(子串的首尾在sentence中的下标)记录。可能这样就可以转化成与会议安排类似的问题:sentence的长度相当于总时间(从0到length-1),一个满足条件的子串看做是一场会议(首字符下标到尾字符下标)。

这样的思路其实并不太可行,在dictionary中查询是否存在特定字符串的过程时间复杂度过大,不经过特殊处理的话是k*n^2。(k是字典中单词个数,n是sentence长度)。同时也没有找到空余时间最少的动态规划方法。

以下实现是看了官方解答后,自己写的不使用字典树的做法。

我的实现

class Solution {
public:
    int respace(vector<string>& dictionary, string sentence) {
        unordered_set<string> dictionaryMap;
        bool p=true;
        for(auto it:dictionary){
            dictionaryMap.insert(it);
        }
        int unused[sentence.size()+1];unused[0]=0;
        for(int i=0;i<sentence.size();i++){
            unused[i+1] = unused[i]+1;
            for(int j=0;j<=i;j++){
                unordered_set<string>::iterator strIt = dictionaryMap.find(sentence.substr(j,i-j+1));
                if(strIt!=dictionaryMap.end()){//在集合中找到了
                    unused[i+1] = min(unused[i+1] , unused[j]);
                }else{//没找到
                    //unused[i+1] = unused[i]+1;
                }
            }
            //printf("no.:%d	%d
",i+1,unused[i+1]);
        }
        //printf("size=%d",sentence.size());
        return unused[sentence.size()];
    }
};
/**
本题有两个关键步骤:
1.找到状态迭代转移规律
2.快速判断一个字符串,是否存在于一个大的字符串集合中
容器相关知识:
1.如何用顺序容器构造关联容器?
2.auto与iterator的区别
3.find函数对于顺序容器和相关容器
4.unordered_map或unordered_set(哈希算法)查找比vector(动态数组)和set(红黑树)快很多。各个容器的实现机制https://www.cnblogs.com/fangrong/p/5271676.html
*/

时间复杂度:m2+|dictionary|。|dictionary|是词典中的字符总数,m是sentence长度

空间复杂度:|dictionary|*S+m。S是字符种类数。节点数一定小于|dictionary|

使用哈希表的做法如下

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 t = s[i] - 'a';
            if (curPos->next[t] == nullptr) {
                curPos->next[t] = new Trie();
            }
            curPos = curPos->next[t];
        }
        curPos->isEnd = true;
    }
};

class Solution {
public:
    int respace(vector<string>& dictionary, string sentence) {
        int n = sentence.length(), inf = 0x3f3f3f3f;

        Trie* root = new Trie();
        for (auto& word: dictionary) {
            root->insert(word);
        }

        vector<int> dp(n + 1, inf);
        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];
    }
};

/**作者:LeetCode-Solution
链接:https://leetcode-cn.com/problems/re-space-lcci/solution/hui-fu-kong-ge-by-leetcode-solution/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。*/

使用字典树(未自己独立实现)

拓展学习

转移方程

动态规划的关键是找到状态转移方程

字典树

熟悉下面的实现

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 t = s[i] - 'a';
            if (curPos->next[t] == nullptr) {
                curPos->next[t] = new Trie();
            }
            curPos = curPos->next[t];
        }
        curPos->isEnd = true;
    }
};

/**作者:LeetCode-Solution
链接:https://leetcode-cn.com/problems/re-space-lcci/solution/hui-fu-kong-ge-by-leetcode-solution/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。*/

Rabin-Karp算法:字符串匹配问题

原文地址:https://www.cnblogs.com/BoysCryToo/p/13275277.html