leetcode 95. Unique Binary Search Trees II、140. Word Break II(自顶向下解决问题)

思考自顶向下解决问题的模式

95. Unique Binary Search Trees

题目链接

leetcode 95. Unique Binary Search Trees

题意

给定一个整数n,输出把1到n所有值作为树结点的所有可能的二叉搜索树的组合。

思路

二叉搜索树的定义:左边结点的值 < 根节点的值 < 右边结点的值。那么根据根节点的不同,1到n的所有值尽可能作为根节点。这里我们不妨假设k(1<=k<=n)为某一颗二叉搜索树的根节点。那么左子树的根节点的可能值为[1,k-1],右子树的根节点的所有可能值为[k+1,n]。

难点在于怎么在[1,k-1],[k+1,n]继续遍历子树的所有左右子树,直接列举所有情况显然是不现实的,这时候我们采用自顶向下的思想,将[1,k-1],[k+1,n]的所有情况放在栈(存储子问题用到的临时变量)中,最后逐步合并所有可能的情况即可。

class Solution {
public:
    //分治算法
    vector<TreeNode*> generateTrees(int n){
        vector<TreeNode*> ans;
        if(n==0) return ans;
        
        return solve(1,n);
    }
    
    vector<TreeNode*> solve(int l,int r){
        vector<TreeNode*> ans;//通过栈区的临时变量存储左右根节点的信息
        if(l>r){
            ans.push_back(NULL);
            return ans;
        }
        
        for(int i=l;i<=r;i++){
            // TreeNode* root=new TreeNode(i);
            vector<TreeNode*> left=solve(l,i-1);
            vector<TreeNode*> right=solve(i+1,r);
            
            //自顶向下思考问题
            for(TreeNode * le: left){
                for(TreeNode *ri:right){
                    
                    TreeNode* root=new TreeNode(i);
                    
                    root->left=le;
                    root->right=ri;
                    
                    ans.push_back(root);//存储子问题的所有可能状态
                }
            }
                
        }
        
        return ans;
    }
    

140. Word Break II

题目链接

leetcode 140. Word Break II

题意

给定一个字典,判断一个字符串能被断句成多少种可能的情况

思路
  1. 首先利用一个set保存词典里面所有出现的词,这样在后续断句的时候能以O(1)的复杂度判断单词是否在词典中
  2. 然后就是断句,给定一个字符串s = “catsanddog”,给定词典 wordDict = [“cat”, “cats”, “and”, “sand”, “dog”],如何寻求所有可能的断句方式?我们可以采用类似上面一题的思想,将这个单词先分成左右两部分,比如将s分成"cat"和"sanddog",那么原问题就变成判断"cat"是否在词典wordDict中,以及"sanddog"的子字符串是否在词典中,这样依次类推下去。
  3. 对于本题还要采用记忆化搜索剪枝(用一个map存储一下已经遍历字符串的结果),避免右边部分字符串的子问题反复判断是否在词典中
class Solution {
public:
    //好题
    //学习自顶向下思考问题的模式
    
    //给定词库 给样例单词做分词
    //暴力O(N^2M)
    //如何高效判断单词的匹配是否正确
    //怎么确定分词位置?
    
    //与判断不同类型的二叉树的题 处理方法比较类似  有点自顶向下的思想
    //新建一个vector 然后从初始点一步步包括子问题
    
    unordered_map<string,vector<string>> ump;//记忆化搜索 剪枝
    
    vector<string> wordBreak(string s, vector<string>& wordDict) {
        vector<string> ans;
        
        if(!s.size() || !wordDict.size()) return ans;
        
        set<string> S;
        
        for(string str:wordDict){
            if(!S.count(str)){
                S.insert(str);
            }
        }
        
        return solve(s,S);
    }
    
    bool Containr(set<string> S,string str){
        
        for(int i=1;i<=str.size();i++){
            string tmp=str.substr(0,i);
            if(S.count(tmp)) return true;
        }
        return false;
    }
    
    vector<string> solve(string s, set<string> S){
        //记忆化搜索
        if(ump.count(s)) return ump[s];
        
        vector<string> ans;//自顶向下,ans放在栈中,存放临时变量
        
        if(S.count(s)) ans.push_back(s);//全部包含 单独处理
        
        for(int i=1;i<s.size();i++){//长度
            
            string left=s.substr(0,i);
            string right=s.substr(i);
            
            if(S.count(left) && Containr(S,right) ){//左边包含 右边也有可能包含
                
                //子问题
                for(string r: solve(right,S)){//仅仅右边字符串 符合情况的所有字符串情况
                    ans.push_back(left+" "+r);
                }
            }
        }
        
        //存储已经找过的字符串
        ump[s]=ans;
        
        return ans;
    }
};

本题思路参考:https://leetcode.com/problems/word-break-ii/discuss/44179/Slightly-modified-DP-Java-solution

posted @ 2020-10-08 20:24  xzhws  阅读(46)  评论(0编辑  收藏  举报