Showing posts with label DFS+Backtracking. Show all posts
Showing posts with label DFS+Backtracking. Show all posts

LeetCode 488 - Zuma Game


https://leetcode.com/problems/zuma-game/
Think about Zuma Game. You have a row of balls on the table, colored red(R), yellow(Y), blue(B), green(G), and white(W). You also have several balls in your hand.
Each time, you may choose a ball in your hand, and insert it into the row (including the leftmost place and rightmost place). Then, if there is a group of 3 or more balls in the same color touching, remove these balls. Keep doing this until no more balls can be removed.
Find the minimal balls you have to insert to remove all the balls on the table. If you cannot remove all the balls, output -1.
Examples: Input: "WRRBBW", "RB" Output: -1 Explanation: WRRBBW -> WRR[R]BBW -> WBBW -> WBB[B]W -> WW Input: "WWRRBBWW", "WRBRW" Output: 2 Explanation: WWRRBBWW -> WWRR[R]BBWW -> WWBBWW -> WWBB[B]WW -> WWWW -> empty Input:"G", "GGGGG" Output: 2 Explanation: G -> G[G] -> GG[G] -> empty Input: "RBYYBBRRB", "YRBGB" Output: 3 Explanation: RBYYBBRRB -> RBYY[Y]BBRRB -> RBBBRRB -> RRRB -> B -> B[B] -> BB[B] -> empty
Note:
  1. You may assume that the initial row of balls on the table won’t have any 3 or more consecutive balls with the same color.
  2. The number of balls on the table won't exceed 20, and the string represents these balls is called "board" in the input.
  3. The number of balls in your hand won't exceed 5, and the string represents these balls is called "hand" in the input.
  4. Both input strings will be non-empty and only contain characters 'R','Y','B','G','W'.
X.
https://leetcode.com/problems/zuma-game/discuss/97007/Standard-test-program-is-wrong
I tried case "RRWWRRBBRR", "WB". The test program gave the expected answer -1. However, I thought the answer might be 2. Because:
RRWWRRBBRR -> RRWWRRBBR[W]R -> RRWWRRBB[B]RWR -> RRWWRRRWR -> RRWWWR -> RRR -> empty


The possible reason might be the first [W] was inserted but not adjacent to a W in the sequence. I read the description twice but didn't find any condition about it.
http://hankerzheng.com/blog/Leetcode-Zuma-Game-
首先预处理input string, 将桌面上的球转换为一个列表, 列表的每个元素表示连续相同颜色球的颜色和个数, 并且预处理时, 可以直接消除相同颜色超过三个以上的球. 因此, 对于RRBBBGYYWWWYB, 转化后的表示为[["R", 2], ["B", 3], ["G", 1], ["B", 1]]. 当前问题的最优解有以下三种可能性:
  • 将当前区间分割为两个部分, 两个部分的最优解合并, 即可得到当前问题的解
  • 如果当前区间第一个颜色和最后一个颜色相同, 那么第一个色块和最后一个色块可以最后合并. 当前问题的解, 即为中间区间的最优解, 加上第一个色块和最后一个色块合并后新问题的解.
  • 如果当前区间第一个颜色和最后一个颜色相同, 并且两个色块合并后个数小于3, 并且存在中间相同颜色个数为1的色块, 那么这三个分离的色块可以合并并消除. 因此, 问题的解即为分割后, 剩下两个色块的最优解.
上述三种情况涵盖了所有的可能性. 该算法的时间复杂度为O(N^3), 其中N表示色块的个数.
https://angovia.me/2019/01/28/LeetCode-488-Zuma-Game/
https://github.com/cherryljr/LeetCode/blob/master/Zuma%20Game.java
 *  注意Note中的说明,需要重点关注的有两点:
 *      1. 游戏初始的珠子串(Board)中并不会有 >=3 个颜色相同的珠子连在一起,这跟我们玩的游戏还是不同的。
 *      2. 数据规模:珠子串长度最长不会超过 20,手里的珠子最多不会超过 5 个。
 *  根据数据规模,我们基本可以断定这道题目是使用 DFS(Backtracking) 来枚举所有插入位置进行暴力解的。
 *
 * 注意点:
 *  以下代码进行了两个部分的剪枝操作。
 *      剪枝操作1:因为如果有相同颜色珠子连在一起的话,插在它们中任意位置效果都是相同的,
 *      所以我们统一规定插在相同颜色珠子串的最后一个位置。
 *      剪枝操作2:只对插入后会被消去的地方进行插入操作。即:如果本轮操作后,无法消去一段
 *      那么该位置就不进行插入操作。
 *  而本题存在问题的也正是上述的这两个剪枝操作!很明显这是一个贪心的做法,但是这个贪心的策略是错误的。
 *  正确的做法应该是每个位置,都需要进行一遍试探性的插入才行。
 *  举个例子:
 *      board = "RRWWRRBBRR", hand = "WB"
 *  如果按照上述的做法,我们会在 [W] 或者 [B] 的后面插入手中的珠子,但是这样肯定会剩下 [RR] 没法消除
 *  因此会返回 -1. 当其实这个局面是有办法全部消去的,策略如下:
 *      RRWWRRBBRR -> RRWWRRBBR[W]R -> RRWWRRBB[B]RWR -> RRWWRRRWR -> RRWWWR -> RRR -> empty
 *  因此这里的剪枝(贪心)策略是错误的。
 *
 * 大家可能会有疑问,为什么错的还要这么做呢?
 * 因为...这题的标程都是错的啊!!!而且不贪心剪枝的话还过不去!!!坑爹啊!!!
 * 这也能解释为啥这么多人踩这道题目了...

    public int findMinStep(String board, String hand) {
        if (board == null || board.length() == 0) {
            return 0;
        }

        // 为了方面查询,我们需要统计手中珠子的信息(本题中我们可以发射手中的任意一颗珠子)
        Map<Character, Integer> balls = new HashMap<>();
        for (char c : hand.toCharArray()) {
            balls.put(c, balls.getOrDefault(c, 0) + 1);
        }
        return dfs(board, balls);
    }

    // 返回全部清楚当前board所需要的最少珠子,如果无法全部清除返回-1
    private int dfs(String board, Map<Character, Integer> balls) {
        if (board == null || board.length() == 0) {
            return 0;
        }

        int rst = Integer.MAX_VALUE;
        int i = 0, j;
        while (i < board.length()) {
            j = i + 1;
            Character color = board.charAt(i);
            // j一直向后移动,直到 j 越界 或者 指向的珠子颜色与 i 的不同
            while (j < board.length() && color == board.charAt(j)) {
                j++;
            }
            // 需要插入多少颗珠子才能消掉 i~j-1 的珠子
            int costBalls = 3 - (j - i);
            // 手中剩余足够的珠子可供插入
            if (balls.containsKey(color) && balls.get(color) >= costBalls) {
                // 消去 i~j-1 段的珠子,同时因为消去该段会导致两边的两段的珠子碰到一起
                // 可能会引发连续的消除反应,因此实现了 shrink() 来实现这点
                String newBoard = shrink(board.substring(0, i) + board.substring(j));
                // 减去花费掉的珠子数
                balls.put(color, balls.get(color) - costBalls);
                // 进行 DFS 调用子过程,求解全部消去剩下珠子需要的最少珠子数
                int subRst = dfs(newBoard, balls);
                if (subRst >= 0) {
                    // 如果可以被顺利全部消除的话,取最小值即可
                    rst = Math.min(rst, costBalls + subRst);
                }
                // Backtracking 加上之前消耗掉的珠子,进行回溯操作
                balls.put(color, balls.get(color) + costBalls);
            }
            i = j;
        }

        return rst == Integer.MAX_VALUE ? -1 : rst;
    }

    // 消除当前 board 中所有可以消除的珠子串
    private String shrink(String board) {
        int i = 0, j;
        while (i < board.length()) {
            j = i + 1;
            Character color = board.charAt(i);
            while (j < board.length() && color == board.charAt(j)) {
                j++;
            }
            if (j - i >= 3) {
                board = board.substring(0, i) + board.substring(j);
                // 当进行成功消除后,记得将 i 重置回 0 位置,重新进行检查(因为可能发生连锁反应)
                i = 0;
            } else {
                i++;
            }
        }
        return board;
    }
http://www.cnblogs.com/grandyang/p/6759881.html
那么这道题是一种简化版的祖玛游戏,只是一个一维数组,而且通过限定桌面上的球不超过20个,手里的球不超过5个来降低来难度,貌似是在暗示我们可以用暴力搜索法来做。这道题比较使用递归的方法来做,通过遍历所有可能的情况来找出最优解,题目希望我们用最少的球来消掉桌上所有的球,如果不能完全消掉,返回-1。我们使用哈希表来统计手中每种球的个数,然后我们遍历桌上的球,我们找连续相同球的个数,在没有可以消除的情况下,连续的个数只能是1个或2个,然后我们用3减去连续个数,就是我们需要补充的球数以使其可以被消除,那么我们在哈希表表中看我们手中的该类型的球够不够,如果够就表示可以消除,我们在哈希表中减去需要使用掉的球数,然后将消掉的球移除,对新的字符串调用递归,如果可以成功消除,会返回一个结果,该结果加上之前需要的球数用来更新结果res,注意调用完递归要恢复哈希表的状态。还有就是在刚进入递归函数时,我们要检测字符串,去除连续3个相同球的情况,这个去除函数也是个递归函数,写起来很简洁,但是很强大

https://discuss.leetcode.com/topic/79820/short-java-solution-beats-98
int MAXCOUNT = 6;   // the max balls you need will not exceed 6 since "The number of balls in your hand won't exceed 5"

public int findMinStep(String board, String hand) {
    int[] handCount = new int[26];
    for (int i = 0; i < hand.length(); ++i) ++handCount[hand.charAt(i) - 'A'];
    int rs = helper(board + "#", handCount);  // append a "#" to avoid special process while j==board.length, make the code shorter.
    return rs == MAXCOUNT ? -1 : rs;
}
private int helper(String s, int[] h) {
    s = removeConsecutive(s);     
    if (s.equals("#")) return 0;
    int  rs = MAXCOUNT, need = 0;
    for (int i = 0, j = 0 ; j < s.length(); ++j) {
        if (s.charAt(j) == s.charAt(i)) continue;
        need = 3 - (j - i);     //balls need to remove current consecutive balls.
        if (h[s.charAt(i) - 'A'] >= need) {
            h[s.charAt(i) - 'A'] -= need;
            rs = Math.min(rs, need + helper(s.substring(0, i) + s.substring(j), h));
            h[s.charAt(i) - 'A'] += need;
        }
        i = j;
    }
    return rs;
}
//remove consecutive balls longer than 3
private String removeConsecutive(String board) {
    for (int i = 0, j = 0; j < board.length(); ++j) {
        if (board.charAt(j) == board.charAt(i)) continue;
        if (j - i >= 3) return removeConsecutive(board.substring(0, i) + board.substring(j));
        else i = j;
    }
    return board;
}

https://discuss.leetcode.com/topic/75434/recursive-java-solution
    public int findMinStep(String board, String hand) {
        List<Character> boardList = new ArrayList<Character>();
        for (char c : board.toCharArray()) {
            boardList.add(c);
        }
        Map<Character,Integer> handMap = new HashMap<>();
        handMap.put('R',0);
        handMap.put('Y',0);
        handMap.put('B',0);
        handMap.put('G',0);
        handMap.put('W',0);
        for (char h : hand.toCharArray()) {
            handMap.put(h, handMap.get(h) + 1);
        }
        return find(boardList, handMap);
    }
    
    private int find(List<Character> board, Map<Character, Integer> hand) {
        cleanupBoard(board);
        if (board.size() == 0) return 0;
        if (empty(hand)) return -1;
        int count = 0;
        int min = Integer.MAX_VALUE;
        for (int i = 0; i<board.size(); i++) {
            char c = board.get(i);
            count++;
            if (i == board.size() - 1 || board.get(i+1) != c) {
                int missing = 3 - count;
                if (hand.get(c) >= missing) {
                    hand.put(c, hand.get(c) - missing);
                    List<Character> smallerBoard = new ArrayList<>(board);
                    for (int j = 0; j<count; j++) {
                        smallerBoard.remove(i-j);
                    }
                    int smallerFind = find(smallerBoard, hand);
                    if ( smallerFind != -1 ) {
                        min = Math.min(smallerFind + missing, min);
                    }
                    hand.put(c, hand.get(c) + missing);
                }
                count = 0;
            }
        }
        return (min == Integer.MAX_VALUE) ? -1 : min;
    }
    
    private void cleanupBoard(List<Character> board) {
        int count = 0;
        boolean cleaned = false;
        for (int i = 0; i<board.size(); i++) {
            char c = board.get(i);
            count++;
            if (i == board.size() - 1 || board.get(i+1) != c) {
                if (count >= 3) {
                    for (int j = 0; j<count; j++) {
                        board.remove(i-j);
                    }
                    cleaned = true;
                    break;
                }
                count = 0;
            }
        }
        if (cleaned) {
            cleanupBoard(board);
        }
    }
    
    private boolean empty(Map<Character,Integer> hand) {
        for (int val : hand.values()) {
            if (val > 0) return false;
        }
        return true;
    }
X.
下面这种解法也是递归解法,但是思路和上面略有不同,这里我们不使用哈希表,而是使用一个集合,我们遍历手中的所有小球,如果某个小球已经在集合中存在了,说明我们已经处理过该小球了,直接跳过,否则就将该小球加入集合中。然后我们遍历桌上的小球,寻找和当前手中小球一样的位置,然后将手中小球加入当前位置,调用去除重复3个小球的函数,如果此时字符串为0了,说明当前桌上小球已经完全消掉了,返回1,因为我们此时只使用了一个小球;否则就将手中的当前小球去掉,对新的桌面和剩余手中的小球调用递归,如果得到的结果不是-1,我们用此结果加1来更新结果res

    int findMinStep(string board, string hand) {
        int res = INT_MAX;
        unordered_set<char> s;
        for (int i = 0; i < hand.size(); ++i) {
            if (s.count(hand[i])) continue;
            s.insert(hand[i]);
            for (int j = 0; j < board.size(); ++j) {
                if (board[j] != hand[i]) continue;
                string newBoard = board, newHand = hand;
                newBoard.insert(j, 1, hand[i]);
                newBoard = removeConsecutive(newBoard);
                if (newBoard.size() == 0) return 1;
                newHand.erase(i, 1);
                int cnt = findMinStep(newBoard, newHand);
                if (cnt != -1) res = min(res, cnt + 1);
            }
        }
        return res == INT_MAX ? -1 : res;
    }
    string removeConsecutive(string board) {
        for (int i = 0, j = 0; i <= board.size(); ++i) {
            if (i < board.size() && board[i] == board[j]) continue;
            if (i - j >= 3) return removeConsecutive(board.substr(0, j) + board.substr(i));
            else j = i;
        }
        return board;
    }

LeetCode 491 - Increasing Subsequences


https://leetcode.com/problems/increasing-subsequences/
Given an integer array, your task is to find all the different possible increasing subsequences of the given array, and the length of an increasing subsequence should be at least 2 .
Example:
Input: [4, 6, 7, 7]
Output: [[4, 6], [4, 7], [4, 6, 7], [4, 6, 7, 7], [6, 7], [6, 7, 7], [7,7], [4,7,7]]
Note:
  1. The length of the given array will not exceed 15.
  2. The range of integer in the given array is [-100,100].
  3. The given array may contain duplicates, and two equal integers should also be considered as a special case of increasing sequence.
X. DFS
http://www.cnblogs.com/grandyang/p/6388103.html
这道题让我们找出所有的递增子序列,那么我们应该不难想到,这题肯定是要先找出所有的子序列,从中找出递增的。找出所有的子序列的题我们之前也接触过Subsets和Subsets II,那两题不同之处在于数组中有没有重复项。而这道题明显是有重复项的,所以需要用到Subsets II中的解法。我们首先来看一种迭代的解法,对于重复项的处理,最偷懒的方法是使用set,利用其自动去处重复项的机制,然后最后返回时再转回vector即可。由于是找递增序列,所以我们需要对递归函数做一些修改,首先题目中说明了递归序列数字至少两个,所以只有当当前子序列个数大于等于2时,才加入结果。然后就是要递增,如果之前的数字大于当前的数字,那么跳过这种情况,继续循环,
    vector<vector<int>> findSubsequences(vector<int>& nums) {
        set<vector<int>> res;
        vector<int> out;
        helper(nums, 0, out, res);
        return vector<vector<int>>(res.begin(), res.end());
    }
    void helper(vector<int>& nums, int start, vector<int>& out, set<vector<int>>& res) {
        if (out.size() >= 2) res.insert(out);
        for (int i = start; i < nums.size(); ++i) {
            if (!out.empty() && out.back() > nums[i]) continue;
            out.push_back(nums[i]);
            helper(nums, i + 1, out, res);
            out.pop_back();
        }
    }
我们也可以在递归中进行去重复处理,方法是用一个set保存中间过程的数字,如果当前的数字在之前出现过了,就直接跳过这种情况即可
https://blog.csdn.net/magicbean2/article/details/78708889
1、DFS+backtracking:这是稍微有点变形的DFS题目:对于nums中的每个整数,如果符合条件,我们有两种选择:1)将它加入到结果中,然后进行DFS并且回溯;2)忽略它,直接进行DFS。为此我们在dfs的内部设立一个哈希表,表示已经尝试过的整数。这样对于具有重复整数的情况,比如例子中给出的[4, 6, 7, 7],当我们遇到第一个7之后,则包含7的所有升序组合都已经被输出了,那么当遇到第二个7的时候,我们就可以忽略。否则就生成重复结果了。

我发现DFS + BackTracking的题目往往有两种模板,一种是dfs中没有for循环的,也就是只调用DFS的下一层(有点像在dfs内部再进行dfs);另一种是dfs内含有从当前位置到末尾的for循环的,也就是在dfs内,平行的调用dfs(有点像dfs内部再进行一个bfs)。前一种模板适合无局部状态的情况,而后一种模板则比较适合需要记录局部状态的情况,例如本题目中我们需要记录已经采用过的整数。


https://leetcode.com/problems/increasing-subsequences/discuss/97147/Java-solution-beats-100
                // used只用于每一层递归,每次只添加一次,遇到重复的就自动跳过
                // 下一层会重新申请数组,所以并不会影响到下一层加新的重复的元素
while nums is not necessarily sorted but we have to skip duplicates in each recursion, so we use a hash set to record what we have used in this particular recursion
    public List<List<Integer>> findSubsequences(int[] nums) {
        List<List<Integer>> res = new LinkedList<>();
        helper(new LinkedList<Integer>(), 0, nums, res);
        return res; 
    }
    private void helper(LinkedList<Integer> list, int index, int[] nums, List<List<Integer>> res){
        if(list.size()>1) res.add(new LinkedList<Integer>(list));
        Set<Integer> used = new HashSet<>();
        for(int i = index; i<nums.length; i++){
            if(used.contains(nums[i])) continue;
            if(list.size()==0 || nums[i]>=list.peekLast()){
                used.add(nums[i]);
                list.add(nums[i]); 
                helper(list, i+1, nums, res);
                list.remove(list.size()-1);
            }
        }
    } 
https://discuss.leetcode.com/topic/76249/java-20-lines-backtracking-solution-using-set-beats-100

If you use LinkedList instead of ArrayList, you could use its getLast, removeLast methods
     public List<List<Integer>> findSubsequences(int[] nums) {
         Set<List<Integer>> res= new HashSet<List<Integer>>();
         List<Integer> holder = new ArrayList<Integer>();
         findSequence(res, holder, 0, nums);
         List result = new ArrayList(res);
         return result;
     }

    public void findSequence(Set<List<Integer>> res, List<Integer> holder, int index, int[] nums) {
        if (holder.size() >= 2) {
            res.add(new ArrayList(holder));
        }
        for (int i = index; i < nums.length; i++) {
            if(holder.size() == 0 || holder.get(holder.size() - 1) <= nums[i]) {
                holder.add(nums[i]);
                findSequence(res, holder, i + 1, nums);
                holder.remove(holder.size() - 1);
            }
        }
    }
https://discuss.leetcode.com/topic/76211/clean-20ms-solution
http://blog.csdn.net/alwaystry/article/details/54692902
  1.     public List<List<Integer>> findSubsequences(int[] nums) {  
  2.         Set<List<Integer>> list = new HashSet<List<Integer>>();  
  3.         backtrack(list,new ArrayList<>(),nums,0);  
  4.         return new ArrayList(list);  
  5.     }  
  6.     private void backtrack(Set<List<Integer>> list, List<Integer> tempList, int[] nums,int start){  
  7.         if (tempList.size()>1) list.add(new ArrayList<>(tempList));  
  8.         for(int i=start;i<nums.length;i++){  
  9.             if(tempList.size()==0||tempList.get(tempList.size()-1)<=nums[i]){  
  10.                tempList.add(nums[i]);  
  11.                backtrack(list,tempList,nums,i+1);  
  12.                tempList.remove(tempList.size()-1);  
  13.             }  
  14.         }  
  15.     }

https://leetcode.com/problems/increasing-subsequences/discuss/97127/Simple-Python
First build all increasing subsequences regardless of length, then filter out the too short ones.
def findSubsequences(self, nums):
    subs = {()}
    for num in nums:
        subs |= {sub + (num,)
                 for sub in subs
                 if not sub or sub[-1] <= num}
    return [sub for sub in subs if len(sub) >= 2]
X. BFS
https://leetcode.com/problems/increasing-subsequences/discuss/97134/Evolve-from-intuitive-solution-to-optimal
  1. We can also generate all increasing subsequences by adding each number to the current sequencies iteratively and use set to remove duplicates.
    vector<vector<int>> findSubsequences(vector<int>& nums) {
        vector<vector<int>> seq(1);
        set<vector<int>> bst;
        for(int i=0;i<nums.size();i++) {
            int n = seq.size();
            for(int j=0;j<n;j++)
                if(seq[j].empty() || seq[j].back()<=nums[i]) {
                    seq.push_back(seq[j]);
                    seq.back().push_back(nums[i]);
                    if(seq.back().size()>1) bst.insert(seq.back());
                }  
        }
        return vector<vector<int>>(bst.begin(),bst.end());
    }
  1. We can do better by not generating duplicates. When adding a duplicate number to existing sequences, we don't need to add to all sequences because that will create duplicate sequence. We only need to add to the sequences created since adding this number last time.


        vector<vector<int>> findSubsequences(vector<int>& nums) {
            vector<vector<int>> res(1);
            unordered_map<int,int> ht;
            for(int i=0;i<nums.size();i++) {
                int n = res.size();
                int start = ht[nums[i]];
                ht[nums[i]] = n;
                for(int j=start;j<n;j++)
                    if(res[j].empty() || res[j].back()<=nums[i]) {
                        res.push_back(res[j]);
                        res.back().push_back(nums[i]);
                    }  
            }
            for(int i=res.size()-1;i>=0;i--) 
                if(res[i].size()<2) {
                    swap(res[i],res.back());
                    res.pop_back();
                }
            return res;
        }
    
    https://blog.csdn.net/magicbean2/article/details/78708889
    2、前缀生成法:注意到一个升序序列总是可以由升序的前缀加上当前整数组成,所以我们可以采用递推的方式由升序的前缀生成所有的结果。例如对于本题目,我们初始的升序前缀是{},那么随着扫描的进行,结果的变化依次是:

    当扫描到4之后,由升序前缀{{}}生成{4},然后升序前缀更新为{{}, {4}};

    当扫描到6之后,由升序前缀{{}, {4}}生成{{6}, {4, 6}},然后升序前缀更新为{{}, {4}, {6}, {4, 6}};

    当扫描到7之后,由升序前缀{{}, {4}, {6}, {4, 6}}生成{{7}, {4, 7}, {6, 7}, {4, 6, 7}},然后升序前缀更新为{{}, {4}, {6}, {4, 6}, {7}, {4, 7}, {6, 7}, {4, 6, 7}};

    当再次扫描到7之后,由升序前缀{{}, {4}, {6}, {4, 6}, {7}, {4, 7}, {6, 7}, {4, 6, 7}}可以生成{{7}, {4, 7}, {6, 7}, {4, 6, 7}, {7, 7}, {4, 7, 7}, {6, 7, 7}, {4, 6, 7, 7}},然后升序前缀更新为{{}, {4}, {6}, {4, 6}, {7}, {4, 7}, {6, 7}, {4, 6, 7}, {7}, {4, 7}, {6, 7}, {4, 6, 7}, {7, 7}, {4, 7, 7}, {6, 7, 7}, {4, 6, 7, 7}}。注意到背景为灰色的部分是重复元素,它们在set中将会被去重。最后再筛选出长度大于等于2的升序序列即可。


        vector<vector<int>> findSubsequences(vector<int>& nums) {
            set<vector<int>> seqs = {vector<int>(0)};       // increasing prefix
            for (int i = 0; i < nums.size(); i++) {
                vector<vector<int>> built(seqs.size());     // get a copy of seqs
                std::copy(seqs.begin(), seqs.end(), built.begin());
                for (auto seq : built) {
                    if (seq.empty() || nums[i] >= seq.back()) {
                        seq.push_back(nums[i]);
                        seqs.insert(seq);
                    }
                }
            }
            vector<vector<int>> res;
            for (auto seq : seqs)
                if (seq.size() > 1) {
                    res.push_back(seq);
                }
            return res;
        }

    下面我们来看迭代的解法,还是老套路,先看偷懒的方法,用set来去处重复。对于递归的处理方法跟之前相同
        vector<vector<int>> findSubsequences(vector<int>& nums) {
            set<vector<int>> res;
            vector<vector<int>> cur(1);
            for (int i = 0; i < nums.size(); ++i) {
                int n = cur.size();
                for (int j = 0; j < n; ++j) {
                    if (!cur[j].empty() && cur[j].back() > nums[i]) continue;
                    cur.push_back(cur[j]);
                    cur.back().push_back(nums[i]);
                    if (cur.back().size() >= 2) res.insert(cur.back());
                }
            }
            return vector<vector<int>>(res.begin(), res.end());
        }
    我们来看不用set的方法,使用一个哈希表来建立每个数字对应的遍历起始位置,默认都是0,然后在遍历的时候先取出原有值当作遍历起始点,然后更新为当前位置,如果某个数字之前出现过,那么取出的原有值就不是0,而是之前那个数的出现位置,这样就就不会产生重复了,如果不太好理解的话就带个简单的实例去试试吧
        vector<vector<int>> findSubsequences(vector<int>& nums) {
            vector<vector<int>> res, cur(1);
            unordered_map<int, int> m;
            for (int i = 0; i < nums.size(); ++i) {
                int n = cur.size();
                int start = m[nums[i]];
                m[nums[i]] = n;
                for (int j = start; j < n; ++j) {
                    if (!cur[j].empty() && cur[j].back() > nums[i]) continue;
                    cur.push_back(cur[j]);
                    cur.back().push_back(nums[i]);
                    if (cur.back().size() >= 2) res.push_back(cur.back());
                }
            }
            return res;
        }
    https://blog.csdn.net/fuxuemingzhu/article/details/79827505
    http://bookshadow.com/weblog/2017/01/22/leetcode-increasing-subsequences/
    下面是动态规划的代码,使用了一个set来保证不会有重复,然后由于set之中只能放不可变的对象,所以放进去的是元组对象。当我们遍历到nums的每个数字的时候,对set中的所有的元素进行遍历,看每个tuple元素的最后一个数字是否是小于等于当前的num的,如果是的话就把当前的元素添加到原来的tuple的后面。这样的循环虽说稍显复杂,但是竟然也能通过了。。
    def findSubsequences(self, nums): """ :type nums: List[int] :rtype: List[List[int]] """ dp = set() for n in nums: for y in list(dp): if n >= y[-1]: dp.add(y + (n,)) dp.add((n,)) return list(e for e in dp if len(e) > 1)
    https://leetcode.com/problems/increasing-subsequences/discuss/97157/DP-solution%3A-not-as-clean-as-other-Python-solutions-but-beats-99-in-speed
    X. DP
    https://buptwc.com/2018/07/03/Leetcode-491-Increasing-Subsequences/
    1. 我们可以先思考一下LIS问题是怎么处理的,我们使用dp[i]表示前i个数字组成的序列中以nums[i]结尾的最长上升子序列的长度,dp[i] = max([dp[j]+1 for j in range(i) if nums[j] < nums[i]])
    2. 根据这个思路我们可以想到这道题其实也可以用一样的方法,我们使用一个字典d,d[i]表示以nums[i]结尾的上升子序列,初始化所有的d[i] = [[nums[i]]]
    3. 则可获得递推式 d[i].append(l+nums[i]) for j in range(i) if nums[j] <= nums[i] for l in d[j]
    4. 当然严格来说上式不能算是递推式,我们直接用给出的具体实例来看一下是怎么运作的,初始化d = {0:[[4]],1:[[6]],2:[[7]],3:[[7]]},我们从1开始分析,nums[1] > nums[0],所以d[1] = [[6],[4,6]],依次类推d[2] = [[4,7],[6,7],[4,6,7]],d[3] = …
    1. 递推式如分析中所示,但考虑到4,6,7,7,7这种情况,计算第三个7的时候会出现重复情况,所以用一个集合来确保答案中没有重复元素,然后又因为list本身不可hash,所以转化成元祖来进行hash即可
        def findSubsequences(self, nums):
            """
            :type nums: List[int]
            :rtype: List[List[int]]
            """
            # 初始化字典
            d = {i:[[nums[i]]] for i in range(len(nums))}
            for i in range(1,len(nums)):
                for j in range(i):
                    if nums[i] >= nums[j]:
                        for l in d[j]:
                            d[i].append(l+[nums[i]])
            
            s = set()
            res = []
            for i in range(len(nums)):
                for l in d[i]:
                 # 因为字典中本身存在长度为1的list,需要我们排除
                    if len(l) < 2: continue
                    if tuple(l) in s: continue
                    s.add(tuple(l))
                    res.append(l)
            return res


    http://bgmeow.xyz/2017/02/24/LeetCode-491/
    动态规划解法。遍历数组 nums 。用一个 map 来存储 已经出现过的值及其上次出现的位置。
    nums[i] 的结果集等于之前所有结果集 res[i - 1] 加上 res[i - 1] 中所有 List 加上 nums[i] (如果 nums[i] 大于最后的数)再加上:
    • 若 nums[i] 第一次出现,nums[i] 与 map 中所有 key 的对子。
    • 若 map 中已存在 nums[i] ,遍历上次位置 last 到这次位置 curr,若某个值 nums[j] 首次出现的位置 i 比 last 小,说明这个值在上次 nums[i] 以后才有,应该将 (nums[j], nums[i]) 加入集合 res 。
      最后将 (nums[i], i) 存入 map
    本来是用一个 List<List<List<Integer>>> 作为 dp 来做,写完之后发现只要记录一下上一次加的最后一个就好了。
    用 List 超时了,应该是中间判断的时间花了太久,干脆先用 set 存,最后转到 List 里,过了。以为考的就是重复时候的条件,辣鸡。
    public List<List<Integer>> findSubsequences(int[] nums) {
    List<List<Integer>> res = new ArrayList<>();
    if (nums == null || nums.length <= 1) {
    return res;
    }
    int length = nums.length;
    Set<List<Integer>> tempRes = new HashSet<>();
    Set<Integer> numSet = new HashSet<>();
    for (int i = 0; i < length; i++) {
    // for list whose last num is smaller than nums[i], add nums[i] to get a new list.
    for (List<Integer> subset: tempRes) {
    if (subset.get(subset.size() - 1) <= nums[i]) {
    List<Integer> newSet = new ArrayList<>(subset);
    newSet.add(nums[i]);
    res.add(newSet);
    }
    }
    tempRes.addAll(res);
    res.clear();
    // for single num, combine num[i] to get a new list
    for (Integer num: numSet) {
    if (nums[i] >= num) {
    List<Integer> newSet = new ArrayList<>();
    newSet.add(num);
    newSet.add(nums[i]);
    tempRes.add(newSet);
    }
    }
    numSet.add(nums[i]);
    }
    res.addAll(tempRes);
    return res;
    }

    Labels

    LeetCode (1432) GeeksforGeeks (1122) LeetCode - Review (1067) Review (882) Algorithm (668) to-do (609) Classic Algorithm (270) Google Interview (237) Classic Interview (222) Dynamic Programming (220) DP (186) Bit Algorithms (145) POJ (141) Math (137) Tree (132) LeetCode - Phone (129) EPI (122) Cracking Coding Interview (119) DFS (115) Difficult Algorithm (115) Lintcode (115) Different Solutions (110) Smart Algorithm (104) Binary Search (96) BFS (91) HackerRank (90) Binary Tree (86) Hard (79) Two Pointers (78) Stack (76) Company-Facebook (75) BST (72) Graph Algorithm (72) Time Complexity (69) Greedy Algorithm (68) Interval (63) Company - Google (62) Geometry Algorithm (61) Interview Corner (61) LeetCode - Extended (61) Union-Find (60) Trie (58) Advanced Data Structure (56) List (56) Priority Queue (53) Codility (52) ComProGuide (50) LeetCode Hard (50) Matrix (50) Bisection (48) Segment Tree (48) Sliding Window (48) USACO (46) Space Optimization (45) Company-Airbnb (41) Greedy (41) Mathematical Algorithm (41) Tree - Post-Order (41) ACM-ICPC (40) Algorithm Interview (40) Data Structure Design (40) Graph (40) Backtracking (39) Data Structure (39) Jobdu (39) Random (39) Codeforces (38) Knapsack (38) LeetCode - DP (38) Recursive Algorithm (38) String Algorithm (38) TopCoder (38) Sort (37) Introduction to Algorithms (36) Pre-Sort (36) Beauty of Programming (35) Must Known (34) Binary Search Tree (33) Follow Up (33) prismoskills (33) Palindrome (32) Permutation (31) Array (30) Google Code Jam (30) HDU (30) Array O(N) (29) Logic Thinking (29) Monotonic Stack (29) Puzzles (29) Code - Detail (27) Company-Zenefits (27) Microsoft 100 - July (27) Queue (27) Binary Indexed Trees (26) TreeMap (26) to-do-must (26) 1point3acres (25) GeeksQuiz (25) Merge Sort (25) Reverse Thinking (25) hihocoder (25) Company - LinkedIn (24) Hash (24) High Frequency (24) Summary (24) Divide and Conquer (23) Proof (23) Game Theory (22) Topological Sort (22) Lintcode - Review (21) Tree - Modification (21) Algorithm Game (20) CareerCup (20) Company - Twitter (20) DFS + Review (20) DP - Relation (20) Brain Teaser (19) DP - Tree (19) Left and Right Array (19) O(N) (19) Sweep Line (19) UVA (19) DP - Bit Masking (18) LeetCode - Thinking (18) KMP (17) LeetCode - TODO (17) Probabilities (17) Simulation (17) String Search (17) Codercareer (16) Company-Uber (16) Iterator (16) Number (16) O(1) Space (16) Shortest Path (16) itint5 (16) DFS+Cache (15) Dijkstra (15) Euclidean GCD (15) Heap (15) LeetCode - Hard (15) Majority (15) Number Theory (15) Rolling Hash (15) Tree Traversal (15) Brute Force (14) Bucket Sort (14) DP - Knapsack (14) DP - Probability (14) Difficult (14) Fast Power Algorithm (14) Pattern (14) Prefix Sum (14) TreeSet (14) Algorithm Videos (13) Amazon Interview (13) Basic Algorithm (13) Codechef (13) Combination (13) Computational Geometry (13) DP - Digit (13) LCA (13) LeetCode - DFS (13) Linked List (13) Long Increasing Sequence(LIS) (13) Math-Divisible (13) Reservoir Sampling (13) mitbbs (13) Algorithm - How To (12) Company - Microsoft (12) DP - Interval (12) DP - Multiple Relation (12) DP - Relation Optimization (12) LeetCode - Classic (12) Level Order Traversal (12) Prime (12) Pruning (12) Reconstruct Tree (12) Thinking (12) X Sum (12) AOJ (11) Bit Mask (11) Company-Snapchat (11) DP - Space Optimization (11) Dequeue (11) Graph DFS (11) MinMax (11) Miscs (11) Princeton (11) Quick Sort (11) Stack - Tree (11) 尺取法 (11) 挑战程序设计竞赛 (11) Coin Change (10) DFS+Backtracking (10) Facebook Hacker Cup (10) Fast Slow Pointers (10) HackerRank Easy (10) Interval Tree (10) Limited Range (10) Matrix - Traverse (10) Monotone Queue (10) SPOJ (10) Starting Point (10) States (10) Stock (10) Theory (10) Tutorialhorizon (10) Kadane - Extended (9) Mathblog (9) Max-Min Flow (9) Maze (9) Median (9) O(32N) (9) Quick Select (9) Stack Overflow (9) System Design (9) Tree - Conversion (9) Use XOR (9) Book Notes (8) Company-Amazon (8) DFS+BFS (8) DP - States (8) Expression (8) Longest Common Subsequence(LCS) (8) One Pass (8) Quadtrees (8) Traversal Once (8) Trie - Suffix (8) 穷竭搜索 (8) Algorithm Problem List (7) All Sub (7) Catalan Number (7) Cycle (7) DP - Cases (7) Facebook Interview (7) Fibonacci Numbers (7) Flood fill (7) Game Nim (7) Graph BFS (7) HackerRank Difficult (7) Hackerearth (7) Inversion (7) Kadane’s Algorithm (7) Manacher (7) Morris Traversal (7) Multiple Data Structures (7) Normalized Key (7) O(XN) (7) Radix Sort (7) Recursion (7) Sampling (7) Suffix Array (7) Tech-Queries (7) Tree - Serialization (7) Tree DP (7) Trie - Bit (7) 蓝桥杯 (7) Algorithm - Brain Teaser (6) BFS - Priority Queue (6) BFS - Unusual (6) Classic Data Structure Impl (6) DP - 2D (6) DP - Monotone Queue (6) DP - Unusual (6) DP-Space Optimization (6) Dutch Flag (6) How To (6) Interviewstreet (6) Knapsack - MultiplePack (6) Local MinMax (6) MST (6) Minimum Spanning Tree (6) Number - Reach (6) Parentheses (6) Pre-Sum (6) Probability (6) Programming Pearls (6) Rabin-Karp (6) Reverse (6) Scan from right (6) Schedule (6) Stream (6) Subset Sum (6) TSP (6) Xpost (6) n00tc0d3r (6) reddit (6) AI (5) Abbreviation (5) Anagram (5) Art Of Programming-July (5) Assumption (5) Bellman Ford (5) Big Data (5) Code - Solid (5) Code Kata (5) Codility-lessons (5) Coding (5) Company - WMware (5) Convex Hull (5) Crazyforcode (5) DFS - Multiple (5) DFS+DP (5) DP - Multi-Dimension (5) DP-Multiple Relation (5) Eulerian Cycle (5) Graph - Unusual (5) Graph Cycle (5) Hash Strategy (5) Immutability (5) Java (5) LogN (5) Manhattan Distance (5) Matrix Chain Multiplication (5) N Queens (5) Pre-Sort: Index (5) Quick Partition (5) Quora (5) Randomized Algorithms (5) Resources (5) Robot (5) SPFA(Shortest Path Faster Algorithm) (5) Shuffle (5) Sieve of Eratosthenes (5) Strongly Connected Components (5) Subarray Sum (5) Sudoku (5) Suffix Tree (5) Swap (5) Threaded (5) Tree - Creation (5) Warshall Floyd (5) Word Search (5) jiuzhang (5)

    Popular Posts