记忆化搜索 Memorization Search

什么是记忆化搜索

  • 在递归函数中, 在函数返回前,记录函数的返回结果。在下一次以同样参数访问函数时直接返回记录下的结果
  • 也就是对递归树进行剪枝,遇到已经计算过的节点就不再继续往下计算,直接返回储存在hash table中的值

记忆化搜索函数的三个特点

  • 函数有返回值
  • 函数返回结果和输入参数有关,和其他全局状态无关
  • 参数列表中传入哈希表或者其他用于记录计算结果的数据结构

记忆化搜索 vs 动态规划

  • 记忆化搜索是动态规划的一种实现方式, 也就是top-down approach
  • 动态规划的另一种实现方式是多重循环 (找出动态转移方程,循环遍历), 也就是bottom-up approach

三种适用于DP的场景

在这里插入图片描述

三种不适用于DP的场景

在这里插入图片描述

Examples:

Leetcode 140: 单词拆分 II

在这里插入图片描述

/*
利用dfs搜索 + 回溯
对于字符串 ss,如果某个前缀是单词列表中的单词,则拆分出该单词,然后对 s 的剩余部分继续拆分。如果可以将整个字符串 s 拆分成单词列表中的单词,则得到一个句子
Example:
catsanddog
c 不在字典里
ca 不在字典里
cat在字典里,所以将剩余部分 sanddog 送入递归, 进入下一层
以此类推
回溯时将每一层的字符串加入前一层返回的字符串

优化: 记忆化搜索
以上方法在个别测试用例时会超时,所以需要记忆化搜索剪枝
第一次查找后,用一个map记录每个index的答案
下次再遇到就直接用
*/


class Solution {
    public List<String> wordBreak(String s, List<String> wordDict) {
        //用一个map实现记忆化搜索: <index, 这个index之后的所有答案>
        Map<Integer, List<String>> memo = new HashMap<>(); 
        return dfs(s, wordDict, 0, memo);
    }

    public List<String> dfs(String s, List<String> wordDict, int startIndex, Map<Integer, List<String>> memo) {
       //查看目前的startIndex是否已经被查找过,如果是,则直接返回答案
       if (memo.get(startIndex) != null) {
           return memo.get(startIndex);
       }

       //如果startIndex等于length,则说明已经触底,返回空
       if (startIndex == s.length()) {
           List<String> list = new ArrayList<>();
           list.add("");
           return list;
       }


        List<String> ans = new ArrayList<>();

        for (int i = startIndex; i <= s.length(); i++) {
            //如果startIndex到 i - 1 组成的字符串在字典里 (substring方法不包括最后的i,所以for loop是  i <= s.length())
            if (wordDict.contains(s.substring(startIndex, i))) {
                //i为startIndex,进入下一次递归
                List<String> tempList = dfs(s, wordDict, i, memo);
                //处理返回的答案,将startIndex到i组成的字符串加入到 返回的list中的每个字符串
                for (String tempString: tempList) {
                    //如果字符串为空,则后面不能有空格,特殊处理
                    if (tempString == "") {
                        ans.add(s.substring(startIndex, i));
                        continue;
                    }
                    ans.add(s.substring(startIndex, i) + " " + tempString);
                }
            }
            //记录startIndex的所有答案
            memo.put(startIndex, ans);
        }

        return ans;
    }
}

Leetcode 44: 通配符匹配

在这里插入图片描述

/*
本题有两种解法:
第一种:记忆化递归
分别用ss和pp遍历字符串s和p
case 1:如果 ss==pp 或者 pp= "?" 则ss++, pp++, 进入下一层递归
case 2:如果 pp == "*"
        for (int i = s_start; i <= s.length(); i++) {
            将 i 和 pp + 1 送入递归函数, 进行对比
        }

使用两个boolean数组:记录 visited[i][j] 和 results[i][j]

第二种:动态规范
*/


class Solution {
    public boolean isMatch(String s, String p) {
        if (s ==null || p == null) {
            return false;
        }

        boolean [][] visited = new boolean[s.length()][p.length()];
        boolean [][] results = new boolean[s.length()][p.length()];
        return helper(s, 0, p, 0, visited, results);
    }

    public boolean helper(String s, int s_start, String p, int p_start,
                          boolean [][] visited, boolean [][] results) {

        //如果p_start等于p.length,则说明p字符串已经处理完毕,s需要也处理完毕,否则就是false    
        //注意这里是p.length 不是 p.length - 1                 
        if (p_start == p.length()) {
            return s_start == s.length();
        }

        //如果s字符串处理完毕,则p剩下的字符需要全部是 *
        if (s_start == s.length()) {
            return ifAllStar(p, p_start);
        }

        //判断目前的index是否处理过
        if (visited[s_start][p_start]) {
            return results[s_start][p_start];
        }

        char pp = p.charAt(p_start);
        char ss = s.charAt(s_start);
        boolean match = false;


        if (pp != '*') { 
            //如果 ss==pp 或者 pp= "?" 则ss++, pp++, 进入下一层递归
            match = ifSameChar(ss, pp) && helper(s, s_start + 1, p, p_start + 1, visited, results);
        }
        else {
            //这行代码和下面注释掉的fou loop是一个意思, 本质是 将 i不断递增 然后 和 pp + 1 送入递归函数, 进行对比
            //注释掉的代码有点慢
            match = helper(s, s_start, p, p_start + 1, visited, results) || helper(s, s_start + 1, p, p_start, visited, results);
        }

        /*
        else {
            for (int i = s_start; i <= s.length(); i++) {
                match = helper(s, i, p, p_start + 1, visited, results) || match;
            }
        }
        */
        
        results[s_start][p_start] = match;
        visited[s_start][p_start] = true;

        return match;
    }


    private boolean ifAllStar(String p, int p_start) {
        for(int i = p_start; i < p.length(); i++) {
            if (p.charAt(i) != '*') {
                return false;
            }
        }

        return true;
    }

    private boolean ifSameChar(char s, char p) {
        return (s == p || p == '?');
    }
}
  • 3
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值