当前位置:   article > 正文

记忆化搜索 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;
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63

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 == '?');
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/木道寻08/article/detail/998704
推荐阅读
相关标签
  

闽ICP备14008679号