美文网首页
组合 排列 记忆化搜索

组合 排列 记忆化搜索

作者: 谢谢水果 | 来源:发表于2018-11-28 05:08 被阅读0次

Java知识点

//Arrays.fill()
Arrays.fill(array, content);

//StringBuilder删除某个位置的字符
sb.deleteCharAt(index)

一 题目列表

其实就是在做深度优先搜索(遍历)遍历保存路径 然后随时检查当前路径是否符合条件 满足就加在结果中

1 组合

39 Combination Sum
40 Combination Sum II
216 Combination Sum III
78 Subsets
90 Subsets II
lt680. Split String

2 排列

46 Permutations
47 Permutations II
lt10 String Permutation II
51 N-Queens
52 N-Queens II

3 记忆化搜索

44 Wildcard Matching
10 Regular Expression Matching
140 Word Break II

二 组合

39. Combination Sum

元素可以重复使用 求和是target组合

class Solution {
    public List<List<Integer>> combinationSum(int[] candidates, int target) {
        List<List<Integer>> result = new ArrayList<>();
        Arrays.sort(candidates);
        helper(candidates, 0, new ArrayList<Integer>(), result, target);
        return result;
    }
    private void helper(int[] candidates, int index, List<Integer> subset, List<List<Integer>> result, int target){
        //深度拷贝
        if(target==0)
            result.add(new ArrayList<Integer>(subset));
        if(index==candidates.length)
            return;
        for(int i=index; i<candidates.length; i++){
            //去重
            if(i>0 && candidates[i]==candidates[i-1])
                continue;
            if(candidates[i]>target)
                return;
            subset.add(candidates[i]);
            //可以重复使用 所以i不更新
            helper(candidates, i, subset, result, target-candidates[i]);
            subset.remove(subset.size()-1);
            
        }
    }
}

40. Combination Sum II

元素不可以重复使用 求和是target组合

class Solution {
    public List<List<Integer>> combinationSum2(int[] candidates, int target) {
        List<List<Integer>> result = new ArrayList<>();
        if(candidates==null || candidates.length==0)
            return result;
        Arrays.sort(candidates);
        helper(candidates, 0, new ArrayList<Integer>(), result, target);
        return result;
    }
    private void helper(int[] candidates, int index, List<Integer> subset, List<List<Integer>> result, int target){
        if(target==0)
            result.add(new ArrayList<Integer>(subset));
        if(index==candidates.length)
            return;
        for(int i=index; i<candidates.length; i++){
            //同层去重 不同层不去重
            if(i>index && candidates[i]==candidates[i-1])
                continue;
            if(target<candidates[i])
                return;
            subset.add(candidates[i]);
            helper(candidates, i+1, subset, result, target-candidates[i]);
            subset.remove(subset.size()-1);
        }
    }
}

216. Combination Sum III

求k个1-9不同的数组合 和是n

class Solution {
    public List<List<Integer>> combinationSum3(int k, int n) {
        List<List<Integer>> result = new ArrayList<>();
        if(n>45 || k>9 || k<=0)
            return result;
        helper(1, new ArrayList<Integer>(), result, n, k);
        return result;
    }
    private void helper(int index, List<Integer> subset, List<List<Integer>> result, int target, int k){
        if(target==0 && k==0)
            result.add(new ArrayList<Integer>(subset));
        if(index==10)
            return;
        for(int i=index; i<=9; i++){
            if(target<i)
                return;
            subset.add(i);
            helper(i+1, subset, result, target-i, k-1);
            subset.remove(subset.size()-1);
        }
    }
}

78. Subsets

没有重复

class Solution {
    public List<List<Integer>> subsets(int[] nums) {
        List<List<Integer>> result = new ArrayList<>();
        if(nums==null || nums.length==0)
            return result;
        helper(nums, 0, new ArrayList<Integer>(), result);
        return result;
    }
    private void helper(int[] nums, int index, ArrayList<Integer> subset, List<List<Integer>> result){
        result.add(new ArrayList<>(subset));
        if(index>=nums.length)
            return;
        for(int i=index; i<nums.length; i++){
            subset.add(nums[i]);
            helper(nums, i+1, subset, result);
            subset.remove(subset.size()-1);
        }
    }
}

90. Subsets II

又重复

class Solution {
    public List<List<Integer>> subsetsWithDup(int[] nums) {
        List<List<Integer>> result = new ArrayList<>();
        if(nums==null || nums.length==0)
            return result;
        Arrays.sort(nums);
        helper(nums, 0, new ArrayList<Integer>(), result);
        return result;
    }
    private void helper(int[] nums, int index, List<Integer> subset, List<List<Integer>> result){
        result.add(new ArrayList<Integer>( subset));
        if(index==nums.length)
            return;
        for(int i=index; i<nums.length; i++){
            //同层去重 不同层不去重
            if(i>index && nums[i]==nums[i-1])
                continue;
            subset.add(nums[i]);
            helper(nums, i+1, subset, result);
            subset.remove(subset.size()-1);
        }
    }
}

lt680. Split String

Given the string "123"
return [["1","2","3"],["12","3"],["1","23"]]

public class Solution {
    /*
     * @param : a string to be split
     * @return: all possible split string array
     */
    public List<List<String>> splitString(String s) {
        // write your code here
        List<List<String>> result = new ArrayList<>();
        if(s==null )
            return result;
        helper(s, 0, new ArrayList<String>(), result);
        return result;
    }
    private void helper(String s, int index, ArrayList<String> subset, List<List<String>> result){
        if(index==s.length()){
            result.add(new ArrayList<String>(subset));
            return;
        }
        for(int i=index; i<index+2 && i<s.length(); i++){
            subset.add(s.substring(index, i+1));
            helper(s, i+1, subset, result);
            subset.remove(subset.size()-1);
        }
    }
}

组合

46. Permutations

无重复元素

class Solution {
    public List<List<Integer>> permute(int[] nums) {
        List<List<Integer>> result = new ArrayList<>();
        if(nums==null || nums.length==0)
            return result;
        boolean[] visited = new boolean[nums.length];
        helper(nums, visited, new ArrayList<>(), result);
        return result;
    }
    private void helper(int[] nums, boolean[] visited, List<Integer> subset, List<List<Integer>> result){
        if(subset.size()==nums.length){
            result.add(new ArrayList<Integer>(subset));
            return;
        }
        for(int i=0; i<nums.length; i++){
            if(visited[i])
                continue;
            visited[i]=true;
            subset.add(nums[i]);
            helper(nums, visited, subset, result);
            subset.remove(subset.size()-1);
            visited[i]=false;
        }
    }
}

47. Permutations II

有重复元素

class Solution {
    public List<List<Integer>> permuteUnique(int[] nums) {
        List<List<Integer>> result = new ArrayList<>();
        if(nums==null || nums.length==0)
            return result;
        Arrays.sort(nums);
        boolean[] visited = new boolean[nums.length];
        helper(nums, visited, new ArrayList<>(), result);
        return result;
    }
    private void helper(int[] nums, boolean[] visited, List<Integer> subset, List<List<Integer>> result){
        if(subset.size()==nums.length){
            result.add(new ArrayList<Integer>(subset));
            return;
        }
        for(int i=0; i<nums.length; i++){
            if(i>0 && nums[i]==nums[i-1] && !visited[i-1])
                continue;
            if(visited[i])
                continue;
            visited[i]=true;
            subset.add(nums[i]);
            helper(nums, visited, subset, result);
            subset.remove(subset.size()-1);
            visited[i]=false;
        }
    }
}

lt10. String Permutation II

public class Solution {
//Given "aabb", return ["aabb", "abab", "baba", "bbaa", "abba", "baab"].
    /**
     * @param str: A string
     * @return: all permutations
     */
    public List<String> stringPermutation2(String str) {
    public List<String> stringPermutation2(String str) {
        // write your code here
        List<String> results = new ArrayList<>();
        str = sort(str);
        boolean[] visited = new boolean[str.length()];
        helper(str, visited, new StringBuilder(), results);
        return results;
    }
    private String sort(String str){
        char[] chars = str.toCharArray();
        Arrays.sort(chars);
        return new String(chars);
    }
    private void helper(String str, boolean[] visited, StringBuilder sb, List<String> results){
        if(sb.length()==str.length()){
            results.add(new String(sb.toString()));
            return;
        }
        for(int i=0; i<str.length(); i++){
            if(visited[i])
                continue;
            if(i>0 && str.charAt(i)==str.charAt(i-1) && visited[i-1]==false)
                continue;
            sb.append(str.charAt(i));
            visited[i] = true;
            helper(str, visited, sb, results);
            visited[i] = false;
            sb.deleteCharAt(sb.length()-1);
        }
    }
}

51. N-Queens

返回所有棋盘布局

//先用0 - (n-1)的排列表示一个棋盘布局
//0 - (n-1)的排列 index代表row 值代表col
//优点在于 只用考虑斜线冲突 不会有同行或同列冲突
//相当于在找所有排列 只不过每次添加的时候考察是否存在冲突
//然后把这些排列转成棋盘布局
class Solution {
    public List<List<String>> solveNQueens(int n) {
        List<List<Integer>> numListLists = new ArrayList<>();
        Set<Integer> cols = new HashSet<Integer>();
        Set<Integer> left = new HashSet<Integer>();
        Set<Integer> right = new HashSet<Integer>();
        helper(n, cols, left, right, new ArrayList<Integer>(), numListLists);
        return toChessBoards(numListLists, n);
    }
    private void helper(int n, Set<Integer> cols, Set<Integer> left, Set<Integer> right, List<Integer> list, List<List<Integer>> lists){
        if(list.size() == n){
            lists.add(new ArrayList<Integer>(list));
            return;
        }
        for(int i=1; i<=n; i++){
            if(cols.contains(i))
                continue;
            if(left.contains(list.size()+i))
                continue;
            if(right.contains(list.size()-i))
                continue;
            left.add(list.size()+i);
            right.add(list.size()-i);
            cols.add(i);
            list.add(i);
            helper(n, cols, left, right, list, lists);
            list.remove(list.size()-1);
            left.remove(list.size()+i);
            right.remove(list.size()-i);
            cols.remove(i);
        }
    }
    private List<List<String>> toChessBoards(List<List<Integer>> numListLists, int n){
        List<List<String>> results = new ArrayList<>();
        char[] chars = new char[n];
        Arrays.fill(chars, '.');
        for(List<Integer> numList: numListLists){
            List<String> result = new ArrayList<>();
            for(Integer col: numList){
                chars[col-1] = 'Q';
                String temp = new String(chars);
                result.add(temp);
                chars[col-1] = '.';
            }
            results.add(result);
        }
        return results;
    }
}

52. N-Queens II

求可能的布局个数

// \冲突 差相等 n*n  可能的值 -(n-1) - (n-1) 所以加n对应到2n长度的d1
// /冲突 和相等 n*n 可能的值 0-2(n-1) 直接对应长度2n的d2
public class Solution {
    int count = 0;
    public int totalNQueens(int n) {
        Set<Integer> cols = new HashSet<Integer>();
        Set<Integer> left = new HashSet<Integer>();
        Set<Integer> right = new HashSet<Integer>();
        helper(n, cols, left, right, new ArrayList<Integer>());
        return count;
    }
    private void helper(int n, Set<Integer> cols, Set<Integer> left, Set<Integer> right, List<Integer> list){
        if(list.size() == n){
            count++;
        }
        for(int i=1; i<=n; i++){
            if(cols.contains(i) || left.contains(list.size()+i) || right.contains(list.size()-i))
                continue;
            left.add(list.size()+i);
            right.add(list.size()-i);
            cols.add(i);
            list.add(i);
            helper(n, cols, left, right, list);
            list.remove(list.size()-1);
            left.remove(list.size()+i);
            right.remove(list.size()-i);
            cols.remove(i);
        }
    }
}

记忆化搜索

44. Wildcard Matching

(0,0)->(1,1)(1,0)(0,1)
(1,0)->(2,1)(1,1)(2,0)
(0,1)->(1,2)(1,1)(0,2)
可以发现已经有重复 所以使用记忆化搜索避免重复
本质上是在搜索过程中会遇到已访问过的节点
'?' Matches any single character.
'' Matches any sequence of characters (including the empty sequence).
时间复杂度为O(s.length()
p.length())

//时间复杂度为O(s.length()*p.length())
class Solution {
     public boolean isMatch(String s, String p) {
        if (s == null || p == null) {
            return false;
        }
        boolean[][] memo = new boolean[s.length()][p.length()];
        boolean[][] visited = new boolean[s.length()][p.length()];
        return isMatchHelper(s, 0, p, 0, memo, visited);
    }
    
    private boolean isMatchHelper(String s, int sIndex,
                                  String p, int pIndex,
                                  boolean[][] memo,
                                  boolean[][] visited) {
        // 如果 p 从pIdex开始是空字符串了,那么 s 也必须从 sIndex 是空才能匹配上
        if (pIndex == p.length()) {
            return sIndex == s.length();
        }
        
        // 如果 s 从 sIndex 是空,那么p 必须全是 * 
        if (sIndex == s.length()) {
            return allStar(p, pIndex);
        }
        
        if (visited[sIndex][pIndex]) {
            return memo[sIndex][pIndex];
        }
        
        char sChar = s.charAt(sIndex);
        char pChar = p.charAt(pIndex);
        boolean match;
        
        if (pChar == '*') {
            match = isMatchHelper(s, sIndex, p, pIndex + 1, memo, visited) ||
                isMatchHelper(s, sIndex + 1, p, pIndex, memo, visited);
        } else {
            match = charMatch(sChar, pChar) &&
                isMatchHelper(s, sIndex + 1, p, pIndex + 1, memo, visited);
        }
        
        visited[sIndex][pIndex] = true;
        memo[sIndex][pIndex] = match;
        return match;
    }
    
    private boolean charMatch(char sChar, char pChar) {
        return (sChar == pChar || pChar == '?');
    }
    
    private boolean allStar(String p, int pIndex) {
        for (int i = pIndex; i < p.length(); i++) {
            if (p.charAt(i) != '*') {
                return false;
            }
        }
        return true;
    }
}

10. Regular Expression Matching

'.' Matches any single character.
'*' Matches zero or more of the preceding element.

class Solution {
    public boolean isMatch(String s, String p) {
        boolean[][] visited = new boolean[s.length()][p.length()];
        boolean[][] memo = new boolean[s.length()][p.length()];
        return helper(s, 0, p, 0, visited, memo);
    }
    public boolean helper(String s,
                          int indexS,
                          String p,
                          int indexP,
                          boolean[][] visited,
                          boolean[][] memo){
        if(indexS==s.length()){
            return checkEmpty(p, indexP);
        }
        if(indexP==p.length()){
            return indexS==s.length();
        }
        if(visited[indexS][indexP]){
            return memo[indexS][indexP];
        }
        boolean match = false;
        if(indexP<p.length()-1 && p.charAt(indexP+1)=='*'){
            match = helper(s, indexS, p, indexP+2, visited, memo) || (helper(s, indexS+1, p, indexP, visited, memo)&&isMatch(s, indexS, p, indexP));
        }else{
            match = isMatch(s, indexS, p, indexP) && helper(s, indexS+1, p, indexP+1, visited, memo);
        }
        visited[indexS][indexP] = true;
        memo[indexS][indexP] = match;
        return match;
    }
    
    private boolean checkEmpty(String p, int index){
        for(int i=index; i<p.length(); i++){
            if(p.charAt(i)!='*'){
                if(i==p.length()-1 || p.charAt(i+1)!='*')
                    return false;
            }
        }
        return true;
    }
    private boolean isMatch(String s, int indexS, String p, int indexP){
        return s.charAt(indexS)==p.charAt(indexP) || p.charAt(indexP)=='.';
    }
}

131. Palindrome Partitioning

求把一个字符串切割成所有字串都是回文串的所有方式
Input: "aab"
Output:
[ ["aa","b"], ["a","a","b"] ]
abaxxxx
如果不用memo xxxx重复计算
如果用了memo 在第一次迭代xxxx的结果已经计算出来 下次遇到直接返回结果就可以
Time complexity: O(n(2^n))
For a string with length n, there will be (n - 1) intervals between chars.
For every interval, we can cut it or not cut it, so there will be 2^(n - 1) ways to partition the string.
For every partition way, we need to check if it is palindrome, which is O(n).
So the time complexity is O(n
(2^n))
解的个数*构造一个解的时间

class Solution {
    public List<List<String>> partition(String s) {
        // return withMemo(s);
        return withoutMemo(s);
    }
    public List<List<String>> withMemo(String s) {
        List<List<String>> result = new ArrayList<>();
        if(s==null || s.length()==0)
            return result;
        Map<Integer, List<List<String>>> map = new HashMap<>();
        return help(s, 0, map);
    }
    private List<List<String>> help(String s, int index, Map<Integer, List<List<String>>> memo){
        if(index==s.length()){
            List<List<String>> result = new ArrayList<>();
            result.add(new ArrayList<>());
            return result;
        }
        if(memo.containsKey(index)){
            return memo.get(index);
        }
        List<List<String>> results = new ArrayList<>();
        for(int i=index; i<s.length(); i++){
            String prefix = s.substring(index, i+1);
            if(!isPalindrome(prefix)){
                continue;
            }
            List<List<String>> suffixs = help(s, i+1, memo);
            
            for(List<String> list: suffixs){
                List<String> result = new ArrayList<>(list);
                result.add(0, prefix);
                results.add(result);
            }
        }
        memo.put(index, results);
        return results;
    }
    
 
    public List<List<String>> withoutMemo(String s) {
        List<List<String>> result = new ArrayList<>();
        if(s==null || s.length()==0)
            return result;
        helper(s, 0, new ArrayList<String>(), result);
        return result;
    }
    private void helper(String s, int index, List<String> subset, List<List<String>> result){
        if(index==s.length()){
            result.add(new ArrayList<String>(subset));
            return;
        }
        for(int i=index; i<s.length(); i++){
            String temp = s.substring(index, i+1);
            if(!isPalindrome(temp)){
                continue;
            }
            subset.add(temp);
            helper(s, i+1, subset, result);
            subset.remove(subset.size()-1);
        }
    }
    private boolean isPalindrome(String s){
        int left = 0;
        int right = s.length()-1;
        while(left<right){
            if(s.charAt(left)!=s.charAt(right))
                return false;
            left++;
            right--;
        }
        return true;
    }
}

140. Word Break II

最坏情况 所有拆法都符合要求 一共2^(n-1)种拆法 所以最坏时间复杂度O(2^n)
记忆化的优点在于
"aaaaab", with wordDict = ["a", "aa", "aaa", "aaaa", "aaaaa", "aaaaa"],
map中 key
在第一次迭代之后 aaab的分割方案已经求过 在第二次迭代可以使用结果

class Solution {
    public List<String> wordBreak(String s, List<String> wordDict) {
        List<String> result = new ArrayList<>();
        if(s==null || wordDict==null)
            return result;
        //key是string value是所有这个string能够被拆分成的词组组合list
        Map<String, List<String>> memo = new HashMap<>();
        return helper(s, wordDict, memo);
    }
    private List<String> helper(String s, List<String> wordDict, Map<String, List<String>> memo){
        if(memo.containsKey(s))
            return memo.get(s);
        List<String> result = new ArrayList<>();
        if(s.length()==0)
            return result;
        if(wordDict.contains(s))
            result.add(s);
        for (int len = 1; len < s.length(); ++len){
            String word = s.substring(0, len);
            if (!wordDict.contains(word)) {
                continue;
            }
            String suffix = s.substring(len);
            List<String> segmentations = helper(suffix, wordDict, memo);
            
            for (String segmentation: segmentations){
                result.add(word + " " + segmentation);
            }
        }
        memo.put(s, result);
        return result;
    }
}

139. Word Break

class Solution {
    public boolean wordBreak(String s, List<String> wordDict) {
        Set<String> set = new HashSet<>();
        for(String string : wordDict)
            set.add(string);
        return bfsMethod(s, set);
        // return dfsMethod(s, set);
    }
    public boolean dfsMethod(String s, Set<String> dict) {
        // DFS
        Set<Integer> set = new HashSet<Integer>();
        return dfs(s, 0, dict, set);
    }
    
    private boolean dfs(String s, int index, Set<String> dict, Set<Integer> set){
        // base case
        if(index == s.length()) return true;
        // check memory
        //原理是如果曾经访问过 这里再次访问 说明后面的结果肯定是false
        if(set.contains(index)) return false;
        // recursion
        for(int i = index+1;i <= s.length();i++){
            String t = s.substring(index, i);
            if(dict.contains(t))
                if(dfs(s, i, dict, set))
                    return true;
                else
                    set.add(i);
        }
        set.add(index);
        return false;
    }
    
    public boolean bfsMethod(String s, Set<String> dict) {
        if (dict.contains(s)) return true;
        Queue<Integer> queue = new LinkedList<Integer>();
        queue.offer(0);
        // use a set to record checked index to avoid repeated work.
        // This is the key to reduce the running time to O(N^2).
        Set<Integer> visited = new HashSet<Integer>();
        visited.add(0);
        while (!queue.isEmpty()) {
            int curIdx = queue.poll();
            for (int i = curIdx+1; i <= s.length(); i++) {
                if (visited.contains(i)) 
                    continue;
                if (dict.contains(s.substring(curIdx, i))) {
                    if (i == s.length()) 
                        return true;
                    queue.offer(i);
                    visited.add(i);
                }
            }
        }
        return false;
    }
}









































相关文章

  • 组合 排列 记忆化搜索

    Java知识点 一 题目列表 其实就是在做深度优先搜索(遍历)遍历保存路径 然后随时检查当前路径是否符合条件 满足...

  • 时间长了就生疏的排列组合

    排列数:组合数:全排列:排列是先组合再排列: 最基本的排列组合公式,不能忘在脑后,应该熟稔于心。 排列和组合的区...

  • 0-1 knapsack

    递归 注释记忆化搜索 测试用例 背包大小5 耗时 添加记忆化搜索

  • 记忆化搜索

    记忆化搜索的本质特征是:待求解的问题的解就是原问题的子问题的解集的合并。 apparently,这是一个递归的定义...

  • 排列组合公式及排列组合算法

    排列组合公式 排列组合公式/排列组合计算公式 公式P是指排列,从N个元素取M个进行排列。 公式C是指组合,从N个元...

  • 排列组合-js

    排列组合 数学相关知识:5分钟彻底了解排列组合 参考:程序员必备算法——排列组合 排列有序,组合无序 3选2 :排...

  • 排列组合——排列

    学习概率论与数理统计,要用到排列组合的知识,更重要的是要用到排列组合的思维方法,因此,学习概率与统计是很有必要了解...

  • 第7节:行程、排列组合问题

    1、行程问题 2、排列组合 排列 组合 分类问题,加法 分步问题,乘法

  • 排列组合的基本计算公式、排列组合的威力

    欢迎关注公众号:沈阳奥数 今天讲一下如何理解和记忆排列组合的基本计算公式,然后再解释一下为什么推荐用排列组合。 排...

  • 11月29日 文字游戏

    中华上下五千年文化,文字通过排列,组合,可以呈现万千景象。最近,发现一组简单的组合,好有研究价值。 ...

网友评论

      本文标题:组合 排列 记忆化搜索

      本文链接:https://www.haomeiwen.com/subject/ccppqqtx.html