Leetcode139 从回溯到记忆化到动态规划

//给定一个非空字符串 s 和一个包含非空单词的列表 wordDict,判定 s 是否可以被空格拆分为一个或多个在字典中出现的单词。
//
// 说明:
//
//
// 拆分时可以重复使用字典中的单词。
// 你可以假设字典中没有重复的单词。
//
//
// 示例 1:
//
// 输入: s = “leetcode”, wordDict = [“leet”, “code”]
//输出: true
//解释: 返回 true 因为 “leetcode” 可以被拆分成 “leet code”。
//
//
// 示例 2:
//
// 输入: s = “applepenapple”, wordDict = [“apple”, “pen”]
//输出: true
//解释: 返回 true 因为 “applepenapple” 可以被拆分成 “apple pen apple”。
// 注意你可以重复使用字典中的单词。
//
//
// 示例 3:
//
// 输入: s = “catsandog”, wordDict = [“cats”, “dog”, “sand”, “and”, “cat”]
//输出: false
//

  1. 回溯算法
    回溯的一般模板
void backtracking(参数) {
if (终⽌条件) {
存放结果;
return;
}
for (选择:本层集合中元素(树中节点孩⼦的数量就是集合的⼤⼩) ) {
处理节点;
backtracking(路径,选择列表); // 递归
回溯,撤销处理结果
}
}

}

由于本题中不要求存储具体的路径,因此没有路径的撤销操作。
撤销的目的是下一次遍历不受影响,此处只有substring操作,一次操作完后,下次的开头是一样的。即第一层遍历取[0.1],[0,2],[0,3] ,所以没有撤销操作。
如果这个时候有添加路径的要求,那么第一层如果结果为【1,2】,下一次符合要求的为【3】,就要把之前的2去掉。

public boolean wordBreak(String s, List<String> wordDict) {
            if (wordDict == null || wordDict.isEmpty()) {
                return false;
            }
            Set<String> set = new HashSet<>(wordDict);

            return backTrace(s, 0, set);
        }

        private boolean backTrace(String s, int start, Set<String> set) {
            if (s.length() == start) {
                return true;
            }
            for (int i = start; i < s.length(); i++) {
                String substring = s.substring(start, i + 1);
                if (set.contains(substring)) {
                    if (backTrace(s, i + 1, set)) {
                        return true;
                    }
                }

            }
            return false;
        }

改为动态规划
backTrace(s, 0, set)可以看做dp[0]
if (s.length() == start) {
return true;
}
可以看做是dp[n]
由dp[n]–>dp[0]

public boolean wordBreak1(String s, List<String> wordDict) {
            if (wordDict == null || wordDict.isEmpty()) {
                return false;
            }
            Set<String> set = new HashSet<>(wordDict);
            int n = s.length();
            boolean[] dp = new boolean[n + 1];
            dp[n] = true;

            for (int start = n - 1; start >= 0; start--) {
                for (int i = start; i < s.length(); i++) {
                    String substring = s.substring(start, i + 1);
                    if (set.contains(substring)) {

                        if (dp[i + 1]) {
                            dp[start] = true;
                        }
                    }

                }
            }

            return dp[0];
//            return dfs(s, 0, set);


        }

其实动态 规划与记忆化很类似,不过一个用数组来表示, 一个用Hashmap来表示

public boolean wordBreak(String s, List<String> wordDict) {
            if (wordDict == null || wordDict.isEmpty()) {
                return false;
            }
            Set<String> set = new HashSet<>(wordDict);

            return backTrace(s, 0, set, new HashMap<>());
        }

        private boolean backTrace(String s, int start, Set<String> set, Map<Integer, Boolean> map) {

            if (s.length() == start) {
                return true;

            }
            if (map.containsKey(start)) {
                return map.get(start);
            }
            for (int i = start; i < s.length(); i++) {
                String substring = s.substring(start, i + 1);
                if (set.contains(substring)) {
                    if (backTrace(s, i + 1, set, map)) {
                        map.put(start, true);
                        return true;
                    }
                }

            }
            map.put(start, false);
            return false;
        }
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值