Labels

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

Saturday, May 16, 2015

Number of Islands

Given a 2d grid map of '1's (land) and '0's (water), count the number of islands. An island is surrounded by water and is formed by connecting adjacent lands horizontally or vertically. You may assume all four edges of the grid are all surrounded by water.
Example 1:
11110
11010
11000
00000
Answer: 1
Example 2:
11000
11000
00100
00011
Answer: 3

Naive Way: Use DFS to go though all grid cells. Use a hashset to keep visited grid cells. Run time is O(n^2). And space is O(n^2).

 public class Solution {  
   public int numIslands(char[][] grid) {  
     Set<List<Integer>> visited = new HashSet<List<Integer>>();  
     int num = 0;  
     for(int i = 0;i < grid.length;i++)  
       for(int j = 0;j < grid[0].length;j++)  
         num+=dfs(grid,i,j,visited);  
     return num;  
   }  
   private int dfs(char[][] grid, int x, int y, Set<List<Integer>> visited){  
     // edge case  
     if(x < 0 || x >= grid.length || y < 0 || y >= grid[0].length) return 0;  
     // island  
     if(grid[x][y] == '1'){  
       List<Integer> list = new ArrayList<Integer>();  
       list.add(x);  
       list.add(y);  
       if(!visited.contains(list)){  
         visited.add(list);  
         dfs(grid, x-1, y, visited);  
         dfs(grid, x+1, y, visited);  
         dfs(grid, x, y-1, visited);  
         dfs(grid, x, y+1, visited);  
         return 1;  
       }  
     }  
     return 0;  
   }  
 }  

I saw a algorithm in Discuss use the original grid to store visited grid cells. Thus reduce space used to O(1).

Thursday, April 2, 2015

Word Search

Given a 2D board and a word, find if the word exists in the grid.
The word can be constructed from letters of sequentially adjacent cell, where "adjacent" cells are those horizontally or vertically neighboring. The same letter cell may not be used more than once.
For example,
Given board =
[
  ["ABCE"],
  ["SFCS"],
  ["ADEE"]
]
word = "ABCCED", -> returns true,
word = "SEE", -> returns true,
word = "ABCB", -> returns false.


Naive Way: First came up with a DFS method. Need a hashset for each path to store visited positions.

 public class Solution {  
   class Node{  
     int x,y;  
     Node(int x, int y){  
       this.x = x;  
       this.y = y;  
     }  
     @Override  
     public boolean equals(Object other){  
       if (!(other instanceof Node))  
         return false;  
       Node n = (Node)other;  
       return this.x==n.x && this.y==n.y;  
     }  
     @Override  
     public int hashCode(){  
       return x << 16 | y;  
     }  
   }  
   public boolean exist(char[][] board, String word) {  
     for(int i = 0;i < board.length;i++)  
       for(int j = 0;j < board[i].length;j++){  
           Node node = new Node(i,j);  
           Stack<Node> stack = new Stack<Node>();  
           Set<Node> set = new HashSet<Node>();  
           stack.add(node);  
           set.add(node);  
           if(dfs(board, node, word, 0, stack, set))  
             return true;  
         }  
     return false;  
   }  
   private boolean dfs(char[][] board, Node node, String word, int index, Stack<Node> stack, Set<Node> set){  
     /* ending cases */  
     // find a path  
     if(index == word.length()) return true;  
     // invalid node  
     if(node.x < 0 || node.x >= board.length || node.y < 0 || node.y >= board[0].length) return false;  
     // not match  
     if(board[node.x][node.y] != word.charAt(index)) return false;  
     // recusive case  
     Node up = new Node(node.x-1, node.y);  
     if(!set.contains(up)){  
       stack.push(up);  
       set.add(up);  
       if(dfs(board, up, word, index+1, stack, set)) return true;  
       stack.pop();  
       set.remove(up);  
     }  
     Node down = new Node(node.x+1, node.y);  
     if(!set.contains(down)){  
       stack.push(down);  
       set.add(down);  
       if(dfs(board, down, word, index+1, stack, set)) return true;  
       stack.pop();  
       set.remove(down);  
     }  
     Node left = new Node(node.x, node.y-1);  
     if(!set.contains(left)){  
       stack.push(left);  
       set.add(left);  
       if(dfs(board, left, word, index+1, stack, set)) return true;  
       stack.pop();  
       set.remove(left);  
     }  
     Node right = new Node(node.x, node.y+1);  
     if(!set.contains(right)){  
       stack.push(right);  
       set.add(right);  
       if(dfs(board, right, word, index+1, stack, set)) return true;  
       stack.pop();  
       set.remove(right);  
     }  
     return false;  
   }  
 }  

And then, I think I am bring too much extra stuff to a simple problem. Better use a 2D array to record used cell.

 public class Solution {  
   public boolean exist(char[][] board, String word) {  
     // edge case  
     if(board == null || board.length == 0) return false;  
     if(word.length() > board.length*board[0].length) return false;  
     boolean[][] used = new boolean[board.length][board[0].length];  
     for(int i = 0;i < board.length;i++){  
       for(int j = 0;j < board[0].length;j++){  
         used[i][j] = true;  
         if(dfs(board, i, j, word, 0, used)) return true;  
         used[i][j] = false;  
       }  
     }  
     return false;  
   }  
   private boolean dfs(char[][] board, int x, int y, String word, int index, boolean[][] used){  
     // ending case  
     if(index==word.length()) return true;  
     if(board[x][y]!=word.charAt(index)) return false;  
     // recursive call  
     if(x-1 >= 0 && !used[x-1][y]){  
       used[x-1][y] = true;  
       if(dfs(board,x-1,y,word,index+1,used)) return true;  
       used[x-1][y] = false;  
     }  
     if(x+1 < board.length && !used[x+1][y]){  
       used[x+1][y] = true;  
       if(dfs(board,x+1,y,word,index+1,used)) return true;  
       used[x+1][y] = false;  
     }  
     if(y-1 >= 0 && !used[x][y-1]){  
       used[x][y-1] = true;  
       if(dfs(board,x,y-1,word,index+1,used)) return true;  
       used[x][y-1] = false;  
     }  
     if(y+1 < board[0].length && !used[x][y+1]){  
       used[x][y+1] = true;  
       if(dfs(board,x,y+1,word,index+1,used)) return true;  
       used[x][y+1] = false;  
     }  
     return false;  
   }  
 }  


but this code get TLE for the longest "aaa.."" string case. And then I saw others' code and found a modified, much more elegant way.

 public class Solution {  
   public boolean exist(char[][] board, String word) {  
     // edge case  
     if(word == null || board == null || board.length == 0) return false;  
     if(word.length() > board.length*board[0].length) return false;  
     boolean[][] used = new boolean[board.length][board[0].length];  
     for(int i = 0;i < board.length;i++)  
       for(int j = 0;j < board[0].length;j++)  
         if(dfs(board, i, j, word, 0, used)) return true;  
     return false;  
   }  
   private boolean dfs(char[][] board, int x, int y, String word, int index, boolean[][] used){  
     // ending case  
     if(index==word.length()) return true;  
     if(x < 0 || x >= board.length || y < 0 || y >= board[0].length) return false;  
     if(used[x][y] || board[x][y]!=word.charAt(index)) return false;  
     // recursive call  
     used[x][y] = true;  
     if(dfs(board,x-1,y,word,index+1,used)) return true;  
     if(dfs(board,x+1,y,word,index+1,used)) return true;  
     if(dfs(board,x,y-1,word,index+1,used)) return true;  
     if(dfs(board,x,y+1,word,index+1,used)) return true;  
     used[x][y] = false;  
     return false;  
   }  
 }  

Wednesday, April 1, 2015

Combinations

Given two integers n and k, return all possible combinations of k numbers out of 1 ... n.
For example,
If n = 4 and k = 2, a solution is:
[
  [2,4],
  [3,4],
  [2,3],
  [1,2],
  [1,3],
  [1,4],
] 
 
 
Naive Way: A recursive method is straightforward. Need to convey current index, n, current k as parameter to recursive function. It is a DFS idea.

 public class Solution {  
   public List<List<Integer>> combine(int n, int k) {  
     List<List<Integer>> rslt = new ArrayList<List<Integer>>();  
     dfs(new Stack<Integer>(), 1, n, k, rslt);  
     return rslt;  
   }  
   private void dfs(Stack<Integer> path, int index, int n, int k, List<List<Integer>> rslt){  
     // ending case  
     if(k==0){  
       List<Integer> list = new ArrayList<Integer>(path);  
       rslt.add(list);  
       return;  
     }  
     // recursion case  
     for(int i = index;i <= n;i++){  
       path.push(i);  
       dfs(path, i+1, n, k-1, rslt);  
       path.pop();  
     }  
   }  
 }  

An iterative method correspondingly.

 public class Solution {  
   public List<List<Integer>> combine(int n, int k) {  
     List<List<Integer>> rslt = new ArrayList<List<Integer>>();  
     Deque<List<Integer>> gross = new ArrayDeque<List<Integer>>();  
     // edge case   
     if(k==0) return rslt;  
     // initialize the rslt  
     for(int i = 1;i <= n-k+1;i++){  
       List<Integer> list = new ArrayList<Integer>();  
       list.add(i);  
       gross.offerLast(list);  
     }  
     // iteration on k  
     int ind = 1;  
     while(ind++ < k){  
       int length = gross.size();  
       for(int i = 0;i < length;i++){  
         List<Integer> list = gross.pollFirst();  
         int num = list.get(list.size()-1);  
         for(int j = num+1;j <= n;j++){  
           List<Integer> new_list = new ArrayList<Integer>(list);  
           new_list.add(j);  
           gross.offerLast(new_list);  
         }  
       }  
     }  
     // add to result  
     while(!gross.isEmpty()) rslt.add(gross.pollFirst());  
     return rslt;  
   }  
 }  

Sunday, March 22, 2015

Climbing Stairs

You are climbing a stair case. It takes n steps to reach to the top.
Each time you can either climb 1 or 2 steps. In how many distinct ways can you climb to the top?

Naive Way: A DP approach should be able to conquer this problem. The basic logic is

opt[i] // # of distinct ways to reach top
// base case
opt[0] = 1, opt[1] = 1 (edge case should be n=0, return 0)
// iteration
opt[i] = opt[i-1]+opt[i-2]

 public class Solution {  
   public int climbStairs(int n) {  
     // DP  
     // edge case  
     if(n == 0) return 0;  
     int opt[] = new int[n+1];  
     // base case  
     opt[0] = 1;  
     opt[1] = 1;  
     // iteration  
     for(int i = 2;i <= n;i++) opt[i] = opt[i-1]+opt[i-2];  
     return opt[n];  
   }  
 }  

It turns out this follows Fibonacci Sequence. So there is a famous DFS recursive method. Could use path memorizing to reduce time complexity from O(2^n) to O(n).

 public class Solution {  
   public int climbStairs(int n) {  
     // DFS  
     Map<Integer, Integer> map = new HashMap<Integer, Integer>();  
     map.put(0,1);  
     map.put(1,1);  
     return dfs(n, map);  
   }  
   private int dfs(int n, Map<Integer, Integer> map){  
     // fast ending  
     if(map.containsKey(n)) return map.get(n);  
     // recursion  
     int rslt = dfs(n-1, map) + dfs(n-2, map);  
     map.put(n, rslt);  
     return rslt;  
   }  
 }  

Tuesday, March 17, 2015

Combination Sum II

Given a collection of candidate numbers (C) and a target number (T), find all unique combinations in C where the candidate numbers sums to T.
Each number in C may only be used once in the combination.
Note:

  • All numbers (including target) will be positive integers.
  • Elements in a combination (a1, a2, … , ak) must be in non-descending order. (ie, a1a2 ≤ … ≤ ak).
  • The solution set must not contain duplicate combinations.
For example, given candidate set 10,1,2,7,6,1,5 and target 8,
A solution set is:
[1, 7]
[1, 2, 5]
[2, 6]
[1, 1, 6]

Naive Way: It's the same with  combination-sum . Just change the iteration index to index+1.

DFS iterative method

 public class Solution {  
   class SumNode{  
     int index;  
     int sum;  
     List<Integer> path;  
     SumNode(int index, int value, List<Integer> path){  
       this.index = index;  
       this.sum = value;  
       this.path = new ArrayList<Integer>(path);  
     }  
     public void addNumber(int value){  
       this.sum += value;  
       this.path.add(value);  
     }  
   }  
   public List<List<Integer>> combinationSum2(int[] num, int target) {  
     List<List<Integer>> rslt = new ArrayList<List<Integer>>();  
     Set<List<Integer>> set = new HashSet<List<Integer>>();  
     Stack<SumNode> stack = new Stack<SumNode>();  
     Arrays.sort(num);  
     SumNode root = new SumNode(-1, 0, new ArrayList<Integer>());  
     stack.push(root);  
     while(!stack.isEmpty()){  
       SumNode node = stack.pop();  
       for(int i = node.index+1;i < num.length;i++){  
         if(node.sum + num[i] > target) break;  
         SumNode child = new SumNode(i, node.sum, node.path);  
         child.addNumber(num[i]);  
         if(child.sum==target) set.add(child.path);  
         else stack.push(child);  
       }  
     }  
     rslt.addAll(set);  
     return rslt;  
   }  
 }  

BFS iterative method

 public class Solution {  
   class SumNode{  
     int index;  
     int sum;  
     List<Integer> path;  
     SumNode(int index, int value, List<Integer> path){  
       this.index = index;  
       this.sum = value;  
       this.path = new ArrayList<Integer>(path);  
     }  
     public void addNumber(int value){  
       this.sum += value;  
       this.path.add(value);  
     }  
   }  
   public List<List<Integer>> combinationSum2(int[] num, int target) {  
     List<List<Integer>> rslt = new ArrayList<List<Integer>>();  
     Set<List<Integer>> set = new HashSet<List<Integer>>();  
     Queue<SumNode> queue = new LinkedList<SumNode>();  
     Arrays.sort(num);  
     SumNode root = new SumNode(-1, 0, new ArrayList<Integer>());  
     queue.add(root);  
     while(!queue.isEmpty()){  
       SumNode node = queue.poll();  
       for(int i = node.index+1;i < num.length;i++){  
         if(node.sum + num[i] > target) break;  
         SumNode child = new SumNode(i, node.sum, node.path);  
         child.addNumber(num[i]);  
         if(child.sum==target) set.add(child.path);  
         else queue.add(child);  
       }  
     }  
     rslt.addAll(set);  
     return rslt;  
   }  
 }  

Recursive Method:

 public class Solution {  
   public List<List<Integer>> combinationSum2(int[] num, int target) {  
     List<List<Integer>> rslt = new ArrayList<List<Integer>>();  
     Set<List<Integer>> set = new HashSet<List<Integer>>();  
     Arrays.sort(num);   
     dfs(num, -1, target, 0, new ArrayList<Integer>(), set);  
     rslt.addAll(set);  
     return rslt;   
   }  
   private void dfs(int[] n, int index, int target, int sum, List<Integer> path, Set<List<Integer>> set){   
    // ending case   
    if(sum==target){set.add(path); return;}   
    // recursion   
    for(int i = index+1;i < n.length;i++){   
     if(n[i]+sum > target) break;   
     List<Integer> list = new ArrayList<Integer>(path);   
     list.add(n[i]);   
     dfs(n, i, target, sum+n[i], list, set);   
    }   
   }   
 }  

Notice: Since all the above solution requires sorting at first. The time complexity is O(nlogn). Space is O(n!).

Improved Way: Can I apply iterative method without using extra class (SumNode in the above code).
If I directly use List<Integer> as nodes, I need to find a way to store the sum of the list and the current index.  I could use the first element to store the sum, the second element to store the current index.

 public class Solution {  
   public List<List<Integer>> combinationSum2(int[] num, int target) {  
     List<List<Integer>> rslt = new ArrayList<List<Integer>>();  
     Set<List<Integer>> set = new HashSet<List<Integer>>();  
     Stack<List<Integer>> stack = new Stack<List<Integer>>();  
     Arrays.sort(num);  
     // initial list  
     List<Integer> root = new ArrayList<Integer>();  
     root.add(0);  
     root.add(-1);  
     // DFS  
     stack.push(root);  
     while(!stack.isEmpty()){  
       List<Integer> list = stack.pop();  
       // check if target found  
       if(list.get(0)==target){  
         List<Integer> path = new ArrayList<Integer>();  
         for(int i = 0;i < list.size()-2;i++)  
           path.add(list.get(i+2));  
         set.add(path);  
       }  
       // push child list  
       for(int i = list.get(1)+1;i < num.length;i++){  
         if(list.get(0)+num[i] > target) break;  
         List<Integer> path = new ArrayList<Integer>(list);  
         path.set(0, path.get(0)+num[i]);  
         path.set(1, i);  
         path.add(num[i]);  
         stack.push(path);  
       }  
     }  
     rslt.addAll(set);  
     return rslt;   
   }  
 }  

Combination Sum

Given a set of candidate numbers (C) and a target number (T), find all unique combinations in C where the candidate numbers sums to T.
The same repeated number may be chosen from C unlimited number of times.
Note:

  • All numbers (including target) will be positive integers.
  • Elements in a combination (a1, a2, … , ak) must be in non-descending order. (ie, a1a2 ≤ … ≤ ak).
  • The solution set must not contain duplicate combinations.
For example, given candidate set 2,3,6,7 and target 7,
A solution set is:
[7]
[2, 2, 3]

Naive Way: Make a recursive function to DFS on all possible combinations. And sort the entire array at first helps to keep numbers in order.

 public class Solution {  
   public List<List<Integer>> combinationSum(int[] candidates, int target) {  
     List<List<Integer>> rslt = new ArrayList<List<Integer>>();  
     Arrays.sort(candidates);  
     dfs(candidates, 0, target, 0, new ArrayList<Integer>(), rslt);  
     return rslt;  
   }  
   private void dfs(int[] n, int index, int target, int sum, List<Integer> path, List<List<Integer>> rslt){  
     // ending case  
     if(sum==target){rslt.add(path); return;}  
     // recursion  
     for(int i = index;i < n.length;i++){  
       if(n[i]+sum > target) break;  
       List<Integer> list = new ArrayList<Integer>(path);  
       list.add(n[i]);  
       dfs(n, i, target, sum+n[i], list, rslt);  
     }  
   }  
 }  

The corresponding iterative way is using a stack to implement DFS.

 public class Solution {  
   class SumNode{  
     int index;  
     int sum;  
     List<Integer> path;  
     SumNode(int index, int value, List<Integer> path){  
       this.index = index;  
       this.sum = value;  
       this.path = new ArrayList<Integer>(path);  
     }  
     public void addNumber(int value){  
       this.sum += value;  
       this.path.add(value);  
     }  
   }  
   public List<List<Integer>> combinationSum(int[] candidates, int target) {  
     List<List<Integer>> rslt = new ArrayList<List<Integer>>();  
     Stack<SumNode> stack = new Stack<SumNode>();  
     Arrays.sort(candidates);  
     SumNode root = new SumNode(0, 0, new ArrayList<Integer>());  
     stack.push(root);  
     while(!stack.isEmpty()){  
       SumNode node = stack.pop();  
       for(int i = node.index;i < candidates.length;i++){  
         if(node.sum + candidates[i] > target) break;  
         SumNode child = new SumNode(i, node.sum, node.path);  
         child.addNumber(candidates[i]);  
         if(child.sum==target) rslt.add(child.path);  
         else stack.push(child);  
       }  
     }  
     return rslt;  
   }  
 }  

And I though about it for a while and tried BFS on it. (Just change the stack to queue). It works. DFS and BFS are two traversal methods on this problem.

 public class Solution {  
   class SumNode{  
     int index;  
     int sum;  
     List<Integer> path;  
     SumNode(int index, int value, List<Integer> path){  
       this.index = index;  
       this.sum = value;  
       this.path = new ArrayList<Integer>(path);  
     }  
     public void addNumber(int value){  
       this.sum += value;  
       this.path.add(value);  
     }  
   }  
   public List<List<Integer>> combinationSum(int[] candidates, int target) {  
     List<List<Integer>> rslt = new ArrayList<List<Integer>>();  
     Queue<SumNode> queue = new LinkedList<SumNode>();  
     Arrays.sort(candidates);  
     SumNode root = new SumNode(0, 0, new ArrayList<Integer>());  
     queue.add(root);  
     while(!queue.isEmpty()){  
       SumNode node = queue.poll();  
       for(int i = node.index;i < candidates.length;i++){  
         if(node.sum + candidates[i] > target) break;  
         SumNode child = new SumNode(i, node.sum, node.path);  
         child.addNumber(candidates[i]);  
         if(child.sum==target) rslt.add(child.path);  
         else queue.add(child);  
       }  
     }  
     return rslt;  
   }  
 }  

Wednesday, March 4, 2015

Sudoku Solver

Write a program to solve a Sudoku puzzle by filling the empty cells.
Empty cells are indicated by the character '.'.
You may assume that there will be only one unique solution.


A sudoku puzzle...


...and its solution numbers marked in red. 


Naive Way: The only way I can solve this kind of problem is DFS, which is n organized brute force solution. However, brute force doesn't not mean a bad method. And how to write this backtrace DFS neatly is really a challenge.

This is the code I first time write it. Even myself don't want to read through it for it is extremely tedious.

 public class Solution {  
   public void solveSudoku(char[][] board) {  
     Stack<Node> stack = new Stack<Node>();  
     // make a copy of board  
     char[][] origin = new char[9][9];  
     Node last = new Node(0,0,'.');  
     for(int i = 0;i < 9;i++)  
       for(int j = 0;j < 9;j++)  
         origin[i][j] = board[i][j];  
     // push the first empty grid node into stack  
     boolean find = false;  
     for(int i = 0;i < 9;i++){  
       for(int j = 0;j < 9;j++){  
         if(board[i][j] == '.'){  
           List<Node> list = validNumber(board,i,j);  
           for(int t = 0;t < list.size();t++)  
             stack.push(list.get(t));  
           find = true;  
           break;  
         }  
       }  
       if(find)  
         break;  
     }  
     // find the last empty grid node  
     find = false;  
     for(int i = 8;i >=0;i--){  
       for(int j = 8;j >= 0;j--){  
         if(board[i][j] == '.'){  
           last = new Node(i,j,'.');  
           find = true;  
           break;  
         }  
       }  
       if(find)  
         break;  
     }  
     // DFS  
     while(!stack.isEmpty()){  
       Node node = stack.pop();  
       board[node.x][node.y] = node.val;  
       if(node.x == last.x && node.y == last.y)  
         return;  
       // find the next position  
       boolean isFound = false;  
       boolean hasNext = false;  
       for(int j = node.y+1;j < 9;j++){ // find on current row  
         if(board[node.x][j]=='.'){  
           isFound = true;  
           List<Node> list = validNumber(board,node.x,j);  
           for(int t = 0;t < list.size();t++)  
             stack.push(list.get(t));  
           if(list.size()!=0)  
             hasNext = true;  
           break;  
         }  
       }  
       if(!isFound){ // find on next rows  
         for(int i = node.x+1;i < 9;i++){  
           for(int j = 0;j < 9;j++){  
             if(board[i][j]=='.'){  
               isFound = true;  
               List<Node> list = validNumber(board,i,j);  
               for(int t = 0;t < list.size();t++)  
                 stack.push(list.get(t));  
               if(list.size()!=0)  
                 hasNext = true;  
               break;  
             }  
           }  
           if(isFound)  
             break;  
         }  
       }  
       if(!isFound) // should never happen  
         return;  
       // if no char can be filled, BACK TRACE  
       if(!hasNext){  
         if(stack.isEmpty()){  
           return;  
         }else{  
           Node peek = stack.peek();  
           if(peek.x==node.x){ // back trace current row  
             for(int j = node.y;j >= peek.y;j--)  
               board[node.x][j] = origin[node.x][j];  
           }else{ // back trace previous row  
             for(int j = node.y;j >= 0;j--)  
               board[node.x][j] = origin[node.x][j];  
             for(int i = node.x-1;i >= peek.x+1;i--)  
               for(int j = 8;j >= 0;j--)  
                 board[i][j] = origin[i][j];  
             for(int j = 8;j >= peek.y;j--)  
               board[peek.x][j] = origin[peek.x][j];  
           }  
         }  
       }  
     }  
     return;  
   }  
   class Node{  
     int x;  
     int y;  
     char val;  
     Node(int a,int b, char v){  
       x = a;  
       y = b;  
       val = v;  
     }  
   }  
   private List<Node> validNumber(char[][] board, int x, int y){  
     List<Node> output = new ArrayList<Node>();  
     boolean seq[] = new boolean[9];  
     // its block  
     int center_x = x/3 * 3 + 1;  
     int center_y = y/3 * 3 + 1;  
     for(int i = -1;i <= 1;i++)  
       for(int j = -1;j <= 1;j++)  
         if(board[center_x+i][center_y+j]!='.')  
           seq[(int)(board[center_x+i][center_y+j]-'1')] = true;  
     // its col and row  
     for(int i = 0;i < 9;i++)  
       if(board[i][y]!='.')  
         seq[(int)(board[i][y]-'1')] = true;  
     for(int j = 0;j < 9;j++)  
       if(board[x][j]!='.')  
         seq[(int)(board[x][j]-'1')] = true;  
     // construct Nodes  
     for(int i = 0;i < 9;i++)  
       if(!seq[i]){  
         Node node = new Node(x,y,(char)(i+'1'));  
         output.add(node);  
       }  
     return output;  
   }  
 }  

Below is my current code, which is more readable, (at least I think so).

 public class Solution {  
   boolean found;  
   public void solveSudoku(char[][] board) {  
     found = false;  
     dfs(board, 0, 0);  
   }  
   private void dfs(char[][] board, int x, int y){  
     // position correction  
     if(x==9){  
       x = 0;  
       y++;  
     }  
     // base case  
     if(y==9) {found = true;return;}  
     // recursion  
     if(board[x][y]!='.'){  
       dfs(board, x+1, y);  
     }else{  
       List<Integer> list = validNum(board, x, y);  
       for(int i = 0;i < list.size();i++){  
         board[x][y] = (char)('0' + list.get(i));  
         dfs(board, x+1, y);  
         if(found) return;  
         board[x][y] = '.';  
       }  
     }  
   }  
   private List<Integer> validNum(char[][] board, int x, int y){  
     List<Integer> list = new ArrayList<Integer>();  
     int center_x = x/3 * 3 + 1;  
     int center_y = y/3 * 3 + 1;  
     boolean[] engaged = new boolean[9];  
     for(int i = -1;i <= 1;i++)  
       for(int j = -1;j <= 1;j++)  
         if(board[center_x+i][center_y+j]!='.')  
           engaged[(int)(board[center_x+i][center_y+j]-'1')] = true;  
     for(int i = 0;i < 9;i++)  
       if(board[i][y]!='.') engaged[(int)(board[i][y] - '1')] = true;  
     for(int j = 0;j < 9;j++)  
       if(board[x][j]!='.') engaged[(int)(board[x][j] - '1')] = true;  
     for(int i = 0;i < engaged.length;i++)  
       if(!engaged[i]) list.add(i+1);  
     return list;  
   }  
 }  

Word Break II

Given a string s and a dictionary of words dict, add spaces in s to construct a sentence where each word is a valid dictionary word.
Return all such possible sentences.
For example, given
s = "catsanddog",
dict = ["cat", "cats", "and", "sand", "dog"].
A solution is ["cats and dog", "cat sand dog"].

Naive Way:  Given word-break has both DP and DFS solutions. I think this question can also be solved by both methods.

First thinking about DP.
Once I got the 1D array denoting whether S[0...i] can be reconstructed by words in dict. After knowing that s[0...i] is re-constructable, need to search for next opt[j] = true (j>i) and dict.contains(s.substring(i,j+1)) = true. A recursive method will be helpful.

This backtrace method didn't get accepted by OJ for TLE. The time consuming for the largest case is really huge. But we still has another backtrace method-> starting from behind rather than starting from beginning.

 public class Solution {  
   public List<String> wordBreak(String s, Set<String> dict) {  
     List<String> rslt = new ArrayList<String>();  
     // edge case   
     if(s==null || s.length()==0) return rslt;   
     // DP   
     boolean opt[] = new boolean[s.length()+1];   
     // base case   
     opt[0] = true;   
     // iteration   
     for(int i = 1;i <= s.length();i++)   
       for(int t = 0;t < i;t++)   
         opt[i] = opt[i] || opt[t] && dict.contains(s.substring(t,i));   
     backtrace(s, opt, 0, dict, new String(), rslt);  
     return rslt;  
   }  
   private void backtrace(String s, boolean[] opt, int index, Set<String> dict, String path, List<String> rslt){  
     // base case  
     if(index == s.length()){  
       rslt.add(path);  
       return;  
     }  
     // recursion  
     StringBuilder str = new StringBuilder();  
     for(int i = index+1; i < opt.length; i++){  
       str.append(s.charAt(i-1));  
       if(opt[i] && dict.contains(str.toString())){  
         String cur = new String(path);  
         cur += path.length()==0?"":" ";  
         cur += str.toString();  
         backtrace(s, opt, i, dict, cur, rslt);  
       }  
     }  
     return;  
   }  
 }  

This starting from end backtrace method get accepted. Why starting from begin have such a difference from starting from end? What I recall from my Algorithm Course is that Michael taught a starting from end backtrace method when backtracing DP. It is because when starting from begin, some middle result, say opt[4] may be true, while starting from 4, no valid result can be generated. We'll waste a lot of time on invalid final result but valid middle result paths. However, starting from end ensures that each path you travel will be a valid path.

 public class Solution {  
   public List<String> wordBreak(String s, Set<String> dict) {  
     List<String> rslt = new ArrayList<String>();  
     // edge case   
     if(s==null || s.length()==0) return rslt;   
     // DP   
     boolean opt[] = new boolean[s.length()+1];   
     // base case   
     opt[0] = true;   
     // iteration   
     for(int i = 1;i <= s.length();i++)   
       for(int t = 0;t < i;t++)   
         opt[i] = opt[i] || opt[t] && dict.contains(s.substring(t,i));   
     backtrace(s, opt, s.length(), dict, new Stack<String>(), rslt);  
     return rslt;  
   }  
   private void backtrace(String s, boolean[] opt, int index, Set<String> dict, Stack<String> path, List<String> rslt){  
     // base case  
     if(index == 0){  
       List<String> list = new ArrayList<String>();  
       StringBuilder str = new StringBuilder();  
       list.addAll(path);  
       for(int i = list.size()-1;i>=0;i--){  
         str.append(list.get(i));  
         if(i!=0) str.append(" ");  
       }  
       rslt.add(str.toString());  
       return;  
     }  
     // recursion  
     StringBuilder str = new StringBuilder();  
     int i = index-1;  
     while(i >= 0){  
       String t = s.substring(i,index);  
       if(opt[i] && dict.contains(t)){  
         path.push(t);  
         backtrace(s, opt, i, dict, path, rslt);  
         path.pop();  
       }  
       i--;  
     }  
     return;  
   }  
 }  


For DFS method, I think the path can be constructed on the fly. Also, doing it from end to start.

 public class Solution {  
   public List<String> wordBreak(String s, Set<String> dict) {  
     List<String> rslt= new ArrayList<String>();  
     // DFS  
     dfs(s, s.length(), dict, new Stack<String>(), rslt);  
     return rslt;  
   }  
   private void dfs(String s, int index, Set<String> dict, Stack<String> path, List<String> rslt){  
     // base case  
     if(index == 0){  
       List<String> list = new ArrayList<String>();  
       StringBuilder str= new StringBuilder();  
       list.addAll(path);  
       for(int i = list.size()-1;i>=0;i--){  
         str.append(list.get(i));  
         if(i!=0) str.append(" ");  
       }  
       rslt.add(str.toString());  
     }  
     // recursion  
     int i = index-1;  
     while(i >= 0){  
       String t = s.substring(i,index);  
       if(dict.contains(t)){  
         path.push(t);  
         dfs(s, i, dict, path, rslt);  
         path.pop();  
       }  
       i--;  
     }  
     return;  
   }  
 }  

It turns out to be the same backtrace method without using DP to generate true or false array. That's nothing weird, it means knowing whether S[0....i] is re-constructable helps little in these test cases. However, that should not be the case, the DP array should be able to provide useful information and deny each path that leads to invalid result. My explanation is the test case for OJ is too weak in Word Break II.

Tuesday, March 3, 2015

Word Break

Given a string s and a dictionary of words dict, determine if s can be segmented into a space-separated sequence of one or more dictionary words.
For example, given
s = "leetcode",
dict = ["leet", "code"].
Return true because "leetcode" can be segmented as "leet code".

Naive Way: Another problem with a dictionary. Since the dictionary is usually large, it's better to do something on the string and left the dictionary to perform dict.contains() function. I came up with a DFS way to cut s and check for the existence of each cut.

DFS first fails for TLE, and then I add a Set to memorize failed index to reduce from O(2^n) to  O(n^2) in run time. This idea is learned from mahdy on his post in Decode Ways

 public class Solution {  
   public boolean wordBreak(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  
     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;  
   }  
 }  

And after a fail test, I found out that each string in the dictionary can be used unlimited times. Which leads a DFS without back trace. If each string in the dictionary can be used only once, a simple modification will solve it.

 public class Solution {  
   public boolean wordBreak(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  
     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)){  
         dict.remove(t);  
         if(dfs(s, i, dict, set))  
           return true;  
         else  
           set.add(i);  
         dict.add(t);  
       }  
     }  
     set.add(index);  
     return false;  
   }  
 }  

But what's the general approach? If it is not for path memorizing, DFS will fail in run time.There should exists a totally different solution that aims to solve this problem.

I am thinking about DP. Because in Decode Ways, the formal method was DP while a DFS with path memorize can deal with it, too.
the basic logic will be
opt[i] // whether s[0....i] can be formed by words in dict
// base case
opt[0] = true
// iteration
opt[i] = opt[t] && dict.contains(s.substring(t+1,i)) for 0<t<i

 public class Solution {  
   public boolean wordBreak(String s, Set<String> dict) {  
     // edge case  
     if(s==null || s.length()==0) return dict.contains("");  
     // DP  
     boolean opt[] = new boolean[s.length()+1];  
     // base case  
     opt[0] = true;  
     // iteration  
     for(int i = 1;i <= s.length();i++)  
       for(int t = 0;t < i;t++)  
         opt[i] = opt[i] || opt[t] && dict.contains(s.substring(t,i));  
     return opt[s.length()];  
   }  
 }  

Monday, March 2, 2015

Regular Expression Matching

Implement regular expression matching with support for '.' and '*'.
'.' Matches any single character.
'*' Matches zero or more of the preceding element.

The matching should cover the entire input string (not partial).

The function prototype should be:
bool isMatch(const char *s, const char *p)

Some examples:
isMatch("aa","a") → false
isMatch("aa","aa") → true
isMatch("aaa","aa") → false
isMatch("aa", "a*") → true
isMatch("aa", ".*") → true
isMatch("ab", ".*") → true
isMatch("aab", "c*a*b") → true 
 
 
Naive Way:  Cam up with DP idea at first glance. Should not be a complicated logic.

opt[i][j] // represents whether S[0....i] matches P[0...j] or not
// base case
opt[0][0] = true;
opt[0][j] = true; if consecutive '.*' or 'x*' are met.
// iteration
opt[i][j] =
if(s[i]==p[j] || p[j] == '.') opt[i-1][j-1]
if(p[j] == '*')
    if(s[i] == p[j-1] || p[j-1] == '.')
        opt[i][j-2]  // don't take s[i] to match p[j-1],p[j]
        || opt[i-1][j] // take s[i] to match p[j-1],p[j]
    else
        opt[i][j-2] // cannot take s[i] to match p[j-1],p[j]

This algorithm is O(nm) run time and space. Can be reduced to O(m) since current column is affected only by previous column.

 public class Solution {  
   public boolean isMatch(String s, String p) {  
     // DP  
     boolean opt[][] = new boolean[s.length()+1][p.length()+1];  
     // base case  
     opt[0][0] = true;  
     boolean valid = false;  
     for(int j = 2;j <= p.length();j+=2){  
       if(p.charAt(j-1)=='*'){ valid = true; opt[0][j] = true;}  
       else{ valid = false;}  
       if(!valid) break;  
     }  
     // iteration  
     for(int i = 1;i <= s.length();i++){  
       for(int j = 1;j <= p.length();j++){  
         opt[i][j] = false;  
         if(s.charAt(i-1)==p.charAt(j-1) || p.charAt(j-1)=='.') opt[i][j] = opt[i-1][j-1];  
         else if(p.charAt(j-1)=='*'){  
           if(s.charAt(i-1)==p.charAt(j-2) || p.charAt(j-2)=='.')  
             opt[i][j] = opt[i-1][j] || opt[i][j-2];  
           else  
             opt[i][j] = opt[i][j-2];  
         }  
       }  
     }  
     return opt[s.length()][p.length()];  
   }  
 }  

Below is a O(n) space version. Could use only one array, but two arrays is more clear.

 public class Solution {  
   public boolean isMatch(String s, String p) {  
     // DP  
     boolean opt[] = new boolean[p.length()+1];  
     boolean pre[] = new boolean[p.length()+1];  
     // base case  
     pre[0] = true;  
     boolean valid = false;  
     for(int j = 2;j <= p.length();j+=2){  
       if(p.charAt(j-1)=='*'){ valid = true; pre[j] = true;}  
       else{ valid = false;}  
       if(!valid) break;  
     }  
     // iteration  
     for(int i = 1;i <= s.length();i++){  
       for(int j = 1;j <= p.length();j++){  
         opt[j] = false;  
         if(s.charAt(i-1)==p.charAt(j-1) || p.charAt(j-1)=='.') opt[j] = pre[j-1];  
         else if(p.charAt(j-1)=='*'){  
           if(s.charAt(i-1)==p.charAt(j-2) || p.charAt(j-2)=='.')  
             opt[j] = pre[j] || opt[j-2];  
           else  
             opt[j] = opt[j-2];  
         }  
       }  
       for(int j = 0;j <= p.length();j++)  
         pre[j] = opt[j];  
     }  
     return pre[p.length()];  
   }  
 }  

There is also a DFS version in Discuss. But the time complexity as far as I am concerned is O(2^n). However the OJ seems just listing as many complicated test cases as possible without large size test cases.

A DFS with path memorizing is as follows.

 public class Solution {  
   public boolean isMatch(String s, String p) {  
     Map<List<Integer>, Boolean> map = new HashMap<List<Integer>, Boolean>();  
     for(int i = p.length();i>=0;i-=2){  
       if(i==p.length() || i < p.length()-1 && p.charAt(i+1)=='*'){  
         List<Integer> list = new ArrayList<Integer>();  
         list.add(s.length());  
         list.add(i);  
         map.put(list, true);  
       }else  
         break;  
     }  
     return isMatch(s.toCharArray(), 0, p.toCharArray(), 0, map);  
   }  
   private boolean isMatch(char[] s, int i, char[] p, int j, Map<List<Integer>, Boolean> map){  
     // check memory  
     List<Integer> list = new ArrayList<Integer>();  
     list.add(i);  
     list.add(j);  
     if(map.containsKey(list)) return map.get(list);  
     // ending case  
     if(i < 0 || j < 0 || j >= p.length) return false;  
     // recursion  
     if(p[j] == '*'){  
       int u = i-2;  
       while(u < s.length && (u==i-2 || s[u] == p[j-1] || p[j-1] == '.'))  
         if(isMatch(s, ++u, p, j+1, map)){   
           map.put(list, true);  
           return true;  
         }  
     }  
     else if(i < s.length && (s[i] == p[j] || p[j] == '.')) return isMatch(s, i+1, p, j+1, map);  
     else if(j+1 < p.length && p[j+1] == '*') return isMatch(s, i, p, j+2, map);  
     map.put(list, false);  
     return false;  
   }  
 }  

Saturday, February 21, 2015

N-Queens II

Follow up for N-Queens problem.
Now, instead outputting board configurations, return the total number of distinct solutions.













Naive Way:在N-queens 中,要求是返回所有解法,那么就知道解法的个数了,但是作为一道新题,是否说明可以用低于返回所有解法的复杂度求得解的个数。

最后还是用了DFS原来的方法,做了一些简化。

 public class Solution {  
   class Node{  
     int x,y;  
     Node(int xx, int yy){  
       x = xx;  
       y = yy;  
     }  
   }  
   public int totalNQueens(int n) {  
     // DFS  
     int board[][] = new int[n][n];  
     int count = 0;  
     Stack<Node> stack = new Stack<Node>();  
     for(int j = 0;j < n;j++)  
       stack.push(new Node(0,j));  
     while(!stack.isEmpty()){  
       Node node = stack.pop();  
       boolean hasNext = false;  
       setBoard(board,node,false);  
       for(int j = 0;j < n;j++)  
         if(node.x+1 < n && board[node.x+1][j] == 0) stack.push(new Node(node.x+1,j));  
       for(int j = 0;j < n;j++)  
         if(node.x+1 < n &&board[node.x+1][j] == 0) hasNext = true;  
       count += node.x==n-1?1:0;  
       if(!hasNext){  
         // back trace  
         if(stack.isEmpty()) break;  
         Node peek = stack.peek();  
         for(int i = node.x;i >= peek.x;i--)  
           for(int j = n-1;j >= 0;j--)  
             if(((i==node.x && j <= node.y) || (i==peek.x && j > peek.y) || (i< node.x && i > peek.x)) && board[i][j]==-1)  
               setBoard(board, new Node(i,j), true);  
       }  
     }  
     return count;  
   }  
   private void setBoard(int[][] b, Node node, boolean clear){  
     int add = clear?-1:1;  
     for(int i = 0;i < b.length;i++)  
       b[i][node.y] +=b[i][node.y]==-1?0:add;  
     for(int j = 0;j < b[0].length;j++)  
       b[node.x][j] +=b[node.x][j]==-1?0:add;  
     for(int i = 0;i < b.length;i++){  
       if(node.x+i < b.length && node.y+i < b[0].length)  
         b[node.x+i][node.y+i] += b[node.x+i][node.y+i]==-1?0:add;  
       if(node.x+i < b.length && node.y-i >= 0)  
         b[node.x+i][node.y-i] += b[node.x+i][node.y-i]==-1?0:add;  
       if(node.x-i >= 0 && node.y+i < b[0].length)  
         b[node.x-i][node.y+i] += b[node.x-i][node.y+i]==-1?0:add;  
       if(node.x-i >= 0 && node.y-i >= 0)  
         b[node.x-i][node.y-i] += b[node.x-i][node.y-i]==-1?0:add;  
     }  
     b[node.x][node.y] = clear?0:-1;  
   }  
 }  



Improved Way:在Discuss看到很多人都不需要用二维矩阵。方法都特别厉害。

https://oj.leetcode.com/discuss/18411/accepted-java-solution
AlexTheGreat 的做法是将所有格子分为col, row, diag1, diag2。col和row 是指列和行。
diag1是指从左上至右下的对角线, diag2是指从左下至右上的对角线。这样就将一个矩阵拆成4个1D向量。
最厉害的还是代码的回溯方式,遍历一个row之后可立即消去之前遍历的痕迹。

 /**  
  * don't need to actually place the queen,  
  * instead, for each row, try to place without violation on  
  * col/ diagonal1/ diagnol2.  
  * trick: to detect whether 2 positions sit on the same diagnol:  
  * if delta(col, row) equals, same diagnol1;  
  * if sum(col, row) equals, same diagnal2.  
  */  
 private final Set<Integer> occupiedCols = new HashSet<Integer>();  
 private final Set<Integer> occupiedDiag1s = new HashSet<Integer>();  
 private final Set<Integer> occupiedDiag2s = new HashSet<Integer>();  
 public int totalNQueens(int n) {  
   return totalNQueensHelper(0, 0, n);  
 }  
 private int totalNQueensHelper(int row, int count, int n) {  
   for (int col = 0; col < n; col++) {  
     if (occupiedCols.contains(col))  
       continue;  
     int diag1 = row - col;  
     if (occupiedDiag1s.contains(diag1))  
       continue;  
     int diag2 = row + col;  
     if (occupiedDiag2s.contains(diag2))  
       continue;  
     // we can now place a queen here  
     if (row == n-1)  
       count++;  
     else {  
       occupiedCols.add(col);  
       occupiedDiag1s.add(diag1);  
       occupiedDiag2s.add(diag2);  
       count = totalNQueensHelper(row+1, count, n);  
       // recover  
       occupiedCols.remove(col);  
       occupiedDiag1s.remove(diag1);  
       occupiedDiag2s.remove(diag2);  
     }  
   }  
   return count;  
 }  



然后还有一些用bit manipulate的做法,我就看懂了一个,觉得这种做法最厉害之处在于不用back trace。来自leetcode用户weird

首先还是定下,col, row, diag1, diag2的4个1D向量, 而row向量会作为recursive的参数成为循环的increment。关键的代码
(col & (cm|dm1|dm2) )==0 
是来判断 当前已知的占据信息 (cm|dm1|dm2) 中,board[col][row]这一点是否被占据了,如果不被占据,即该bit 为0,就可以升一行,并将该点被占据的信息记入cm 中,然后更新 dm1 和dm2。至于dm1 和 dm2 是如何更新的。首先要认清这个回溯算法是不需要back trace的,每次传进 bts ()中的内容都是之前一行的占据情况,同一行不同列使用的信息都是一样的,就是之前一行的占据情况。所以dm1的更新只需要记录对于下一个点会有影响的对角线上的点是否被占据,写一个点必是下一行的某个点,而那时的col会比现在的高一个bit,所以dm1要左移一个bit。简单的说就是cm, dm1,dm2记录的都是相对位置,不是绝对位置。



 public class Solution {  
   int count=0;  
   public int totalNQueens(int n) {  
     bts(0,n,0,0,0);  
     return count;  
   }  
   //passing column mask, left diagonal mask, right diagonal mask  
   void bts(int row, int n, int cm, int dm1, int dm2){  
     if (row==n){  
       count++;  
       return;  
     }  
     for (int col=(1<<(n-1));col>0;col>>=1)  
       if ((col & (cm|dm1|dm2) )==0 )  
         bts(row+1,n, cm|col, (col|dm1)<<1, (col|dm2)>>1);  
   }  
 }   

Wednesday, February 11, 2015

Generate Parentheses


Generate Parentheses



 


Given n pairs of parentheses, write a function to generate all combinations of well-formed parentheses.
For example, given n = 3, a solution set is:
"((()))", "(()())", "(())()", "()(())", "()()()"




Naive Way:由于我已经做过一次了,现在第一反应就是那个最牛的方法,核心是通过插入新的"()"到上一级所有String每一个位置中得到所有新的这一级的String,需要一个Set去掉duplicate。

这个方法因为每个位置都插入可以防止有漏,但同时也带来大量冗余,运行时间比较慢。这道题因为输出是指数级的,算法复杂度应该也是指数级的。

public class Solution {
    public List<String> generateParenthesis(int n) {
        List<String> list = new ArrayList<String>();
        // base case
        if(n==1){
            String s = "()";
            list.add(s);
            return list;
        }
        // recursive
        List<String> preLevel = generateParenthesis(n-1);
        Set<String> set = new HashSet<String>();
        for(int i = 0;i < preLevel.size();i++){
            String orig = preLevel.get(i);
            for(int j = 0;j <= orig.length();j++){
                String s = orig.substring(0,j)+"()"+orig.substring(j,orig.length());
                if(!set.contains(s))
                    set.add(s);
            }
        }
        list.addAll(set);
        return list;
    }
}
 

然后我第一次做是用DP做的。需要存贮每一级的String,空间复杂度极高。基本思路就是
7 = 1+6
7 = 2+5
7 = 3+4
7 = 4+3
...
再加上一个
7 = "("+6+")"

public class Solution {
        public List<String> generateParenthesis(int n) {
            List<List<String>> opt = new ArrayList<List<String>>();
            // base case
            List<String> zero = new ArrayList<String>();
            List<String> one = new ArrayList<String>();
            one.add("()");
            opt.add(zero);
            opt.add(one);
            // iteration
            for(int i = 2;i <= n;i++){
                List<String> current = new ArrayList<String>();
                HashSet<String> visited = new HashSet<String>();
                for(int j = 1;j <= i/2;j++){
                    List<String> lst = combine(opt.get(j), opt.get(i-j), visited);
                    for(int u = 0;u < lst.size();u++)
                        current.add(lst.get(u));
                }
                for(int j = 0;j < opt.get(i-1).size();j++){
                    String outer = "("+opt.get(i-1).get(j)+")";
                    current.add(outer);
                }
                opt.add(current);
            }
           
            return opt.get(n);
        }
       
        private List<String> combine(List<String> a, List<String> b, HashSet<String> visited){
            List<String> output = new ArrayList<String>();
            for(int i = 0;i < a.size();i++){
                for(int j = 0;j < b.size();j++){
                    String left = a.get(i) + b.get(j);
                    String right = b.get(j) + a.get(i);
                    if(!visited.contains(left)){
                        visited.add(left);
                        output.add(left);
                    }
                    if(!visited.contains(right)){
                        visited.add(right);
                        output.add(right);
                    }
                }
            }
            return output;
        }
}


Improved Way:这是我当时看到的最牛的做法,现在已经找不到那个提问了,还好把代码记了下来,是用DFS做的。核心思想就是从一个"("出发,可以从左边加新的"(",也可以从右边加新的")",而这相当于它的两个子节点。然后对这样的一棵树进行DFS。


class Node{
       
        String str; // "(" , ")"s
        int used; // # of "("s used
        int left; // # of "("s need to be matched
        Node(String c,int x, int y){
            str = new String(c);
            used = x;
            left = y;
        }
    }

public List<String> generateParenthesis(int n) {
            List<String> output = new ArrayList<String>();
            Stack<Node> stack = new Stack<Node>();
            Node initial = new Node("(",1,1);
            stack.push(initial);
            while(!stack.isEmpty()){
                Node node = stack.pop();
                // check valid
                if(node.used == n && node.left == 0){
                    output.add(node.str);
                }
               
                // push in next possible Node
                if(node.used < n){
                    Node leftParenNode = new Node(node.str+"(",node.used+1,node.left+1);
                    stack.push(leftParenNode);
                }
                if(node.left > 0){
                    Node rightParenNode = new Node(node.str+")",node.used,node.left-1);
                    stack.push(rightParenNode);
                }
            }
           
            return output;
        }


Sunday, February 1, 2015

Letter Combinations of a Phone Number


Letter Combinations of a Phone Number



 


Given a digit string, return all possible letter combinations that the number could represent.
A mapping of digit to letters (just like on the telephone buttons) is given below.

Input:Digit string "23"
Output: ["ad", "ae", "af", "bd", "be", "bf", "cd", "ce", "cf"].
Note:
Although the above answer is in lexicographical order, your answer could be in any order you want.

Naive Way: 先初始化list,遍历String中所有字符,每经过一个都在原有的list<String>里加入新字符,把每一个新String都加入到list里,然后删除原来list长度的String。这样描述下来,貌似用一个queue来存string会更好。但是运行时间对比下来用queue反而更慢些。

下面是不用queue的。

public class Solution {
    public List<String> letterCombinations(String digits) {
        List<String> list = new ArrayList<String>();
        String zero = "";
        list.add(zero);
        for(int i = 0;i < digits.length();i++){
            String chars = digit2chars(digits.charAt(i));
                int preLen = list.size();
                for(int t = 0;t < preLen;t++){
                    for(int j = 0;j < chars.length();j++){
                        String s = new String(list.get(t)+chars.charAt(j));
                        list.add(s);
                    }
                }
                for(int t = 0;t < preLen;t++){
                    list.remove(0);
                }
        }
        return list;
    }
    
    private String digit2chars(char num){
        switch(num){
            case '1':
                return "";
            case '2':
                return "abc";
            case '3':
                return "def";
            case '4':
                return "ghi";
            case '5':
                return "jkl";
            case '6':
                return "mno";
            case '7':
                return "pqrs";
            case '8':
                return "tuv";
            case '9':
                return "wxyz";
            default:
                return "";
        }
    }
}


下面是用queue的。

public class Solution {
    public List<String> letterCombinations(String digits) {
        List<String> list = new ArrayList<String>();
        Queue<String> queue = new LinkedList<String>();
        String empty = "";
        queue.add(empty);
        for(int i = 0;i < digits.length();i++){
            String chars = digit2chars(digits.charAt(i));
                int preLen = queue.size();
                for(int t = 0;t < preLen;t++){
                    String pre = queue.poll();
                    for(int j = 0;j < chars.length();j++){
                        String s = new String(pre+chars.charAt(j));
                        queue.add(s);
                    }
                }
        }
        list.addAll(queue);
        return list;
    }
}

 

Friday, January 30, 2015

N-Queens


N-Queens



 


The n-queens puzzle is the problem of placing n queens on an n×n chessboard such that no two queens attack each other.

Given an integer n, return all distinct solutions to the n-queens puzzle.
Each solution contains a distinct board configuration of the n-queens' placement, where 'Q' and '.' both indicate a queen and an empty space respectively.
For example,
There exist two distinct solutions to the 4-queens puzzle:
[
 [".Q..",  // Solution 1
  "...Q",
  "Q...",
  "..Q."],

 ["..Q.",  // Solution 2
  "Q...",
  "...Q",
  ".Q.."]
] 
 
Naive Way: 这道题可以直观感受到并不是巧妙的算法的,应该是要brute force遍历所有情况。
对于这种题,类似的还有解数独那道题,我都是想到DFS来解的。
但同样是DFS,解法的复杂性,所用的空间大小,back trace的难度,都是很值得想的。
 
这是我的解法。使用了一个矩阵做容器,DFS遍历,带回溯,每次放置一个Q,对应覆盖位置+1,
回溯时每一个Q的对应覆盖位置-1。每次都检测是否填完,填完则记录进结果。运行时间在所有用Java
的人中排在后面。 



class Node{
        int x, y;
        Node(int a,int b){x = a;y = b;}
    }
    
    public List<String[]> solveNQueens(int n) {
        List<String[]> rlst = new ArrayList<String[]>();
        Stack<Node> stack = new Stack<Node>();
        char[][] board = new char[n][n];
        for(int i = 0;i < n;i++){for(int j = 0;j < n;j++){board[i][j] = '0';}}
        
        // initialize stack
        for(int i = 0;i < n;i++){
            Node node = new Node(0,i);
            stack.push(node);
        }
        
        // DFS
        while(!stack.isEmpty()){
            Node node = stack.pop();
            board[node.x][node.y] = 'Q';
            boolean hasNext = false;
            
            // set new Q
            addBoard(board, node.x, node.y);
            if(isFinished(board) && node.x==n-1){
                String[] strs = new String[n];
                for(int i = 0;i < n;i++){
                    StringBuilder str = new StringBuilder();
                    for(int j = 0;j < n;j++)
                        str.append(board[i][j]=='Q'?'Q':'.');
                    strs[i] = str.toString();
                }
                rlst.add(strs);
            }
                
            // push next row
            if(node.x+1 < n){
                for(int j = 0;j < n;j++){
                    if(board[node.x+1][j]=='0'){
                        Node next = new Node(node.x+1,j);
                        stack.push(next);
                        hasNext = true;
                    }
                }
            }
                
            if(!hasNext || (isFinished(board) && node.x==n-1)){
                // back trace
                if(!stack.isEmpty()){
                    Node peek = stack.peek();
                    for(int i = node.y;i >= 0;i--)
                        if(board[node.x][i]=='Q')
                            minusBoard(board,node.x,i);
                            
                    for(int i = node.x-1;i >= peek.x;i--)
                        for(int j = n-1;j >= 0;j--)
                            if(board[i][j] =='Q')
                                minusBoard(board,i,j);
                }
            }
            
        }
        
        return rlst;
    }
    
    private boolean isFinished(char[][] b){
        for(int i = 0;i < b.length;i++)
            for(int j = 0;j < b[0].length;j++)
                if(b[i][j]== '0')
                    return false;
        return true;
    }
    
    private void addBoard(char[][] b, int x, int y){
        for(int i = 0;i < b.length;i++)
            b[i][y] = b[i][y]=='Q'?b[i][y]:(char)(b[i][y]+1);
        for(int j = 0;j < b[0].length;j++)
            b[x][j] = b[x][j]=='Q'?b[x][j]:(char)(b[x][j]+1);
        for(int i = 1;i < b.length;i++){
            if(x+i >= 0 && x+i < b.length && y+i >= 0 && y+i < b[0].length)
             b[x+i][y+i] = b[x+i][y+i]=='Q'?b[x+i][y+i]:(char)(b[x+i][y+i]+1);
            if(x+i >= 0 && x+i < b.length && y-i >= 0 && y-i < b[0].length)
             b[x+i][y-i] = b[x+i][y-i]=='Q'?b[x+i][y-i]:(char)(b[x+i][y-i]+1);
            if(x-i >= 0 && x-i < b.length && y+i >= 0 && y+i < b[0].length)
             b[x-i][y+i] = b[x-i][y+i]=='Q'?b[x-i][y+i]:(char)(b[x-i][y+i]+1);
            if(x-i >= 0 && x-i < b.length && y-i >= 0 && y-i < b[0].length)
             b[x-i][y-i] = b[x-i][y-i]=='Q'?b[x-i][y-i]:(char)(b[x-i][y-i]+1);
        }
        return;
    }
    
    private void minusBoard(char[][] b, int x, int y){
     for(int i = 0;i < b.length;i++)
            b[i][y] = b[i][y]=='Q'?b[i][y]:(char)(b[i][y]-1);
        for(int j = 0;j < b[0].length;j++)
            b[x][j] = b[x][j]=='Q'?b[x][j]:(char)(b[x][j]-1);
        for(int i = 1;i < b.length;i++){
            if(x+i >= 0 && x+i < b.length && y+i >= 0 && y+i < b[0].length)
             b[x+i][y+i] = b[x+i][y+i]=='Q'?b[x+i][y+i]:(char)(b[x+i][y+i]-1);
            if(x+i >= 0 && x+i < b.length && y-i >= 0 && y-i < b[0].length)
             b[x+i][y-i] = b[x+i][y-i]=='Q'?b[x+i][y-i]:(char)(b[x+i][y-i]-1);
            if(x-i >= 0 && x-i < b.length && y+i >= 0 && y+i < b[0].length)
             b[x-i][y+i] = b[x-i][y+i]=='Q'?b[x-i][y+i]:(char)(b[x-i][y+i]-1);
            if(x-i >= 0 && x-i < b.length && y-i >= 0 && y-i < b[0].length)
             b[x-i][y-i] = b[x-i][y-i]=='Q'?b[x-i][y-i]:(char)(b[x-i][y-i]-1);
        }
        b[x][y] = '0';
        return;
    }
 



Improved Way:N Queens II

 

Monday, January 26, 2015

Word Ladder II


Word Ladder II



 


Given two words (start and end), and a dictionary, find all shortest transformation sequence(s) from start to end, such that:
  1. Only one letter can be changed at a time
  2. Each intermediate word must exist in the dictionary
For example,
Given:
start = "hit"
end = "cog"
dict = ["hot","dot","dog","lot","log"]

Return

  [
    ["hit","hot","dot","dog","cog"],
    ["hit","hot","lot","log","cog"]
  ]
Note:

  • All words have the same length.
  • All words contain only lowercase alphabetic characters.

Naive Way:This question is hard. I tried at least 100 times but only get one of my solution passed the OJ in 1800+ms. Hard problem can always classify people, so I need to pay more attention to this question. In  word-ladder , I use a BFS approach, which is easy and quick to generate. What is different this time is that whether BFS or DFS, we need to mark each node that is visited. Consider this case:

start = 'red'
end = 'tax'
dict = ['ted', rad', 'tad']

since we cannot use a node twice, whether BFS or DFS will give us
either red->ted->tad->tax
or red->rad->tad->tax
because 'tad' is a common word in two paths.

A DFS with back-tracing is able to deal with that. But only DFS cannot ensure minimum steps. Thus, I apply level-order BFS first to put every word in a List<Set<String>> layer structure container, with the size of outer list equal to the minimum step. And apply DFS on this container to get each path. This is my first solution that get accepted. It takes 1400+ ms, while the average run time for this question is around 700ms.

 public class Solution {  
   public List<List<String>> findLadders(String start, String end, Set<String> dict) {  
     List<List<String>> rslt = new ArrayList<List<String>>();  
     List<Set<String>> tree = new ArrayList<Set<String>>();  
     boolean found = false;  
       
     // initialize first layer of the tree  
     Set<String> first_layer = new HashSet<String>();  
     first_layer.add(start);  
     if(dict.contains(start)) dict.remove(start);  
     tree.add(first_layer);  
       
     // add end to dictionary  
     dict.add(end);  
       
     // level-order traversal to construct the tree  
     while(!found && tree.get(tree.size()-1).size()!=0){  
       Set<String> new_layer = new HashSet<String>();  
       Set<String> cur_layer = tree.get(tree.size()-1);  
       Iterator<String> iter = cur_layer.iterator();  
       while(iter.hasNext()){  
         String s = iter.next();  
         char[] chars = s.toCharArray();  
         for(int j = 0;j < s.length();j++){  
           char original = chars[j];  
           for(char c = 'a';c <= 'z';c++){  
             chars[j] = c;  
             String t = new String(chars);  
             if(t.equals(end)) found = true;  
             if(dict.contains(t)){  
               if(!t.equals(end)) dict.remove(t);  
               new_layer.add(t);  
             }  
           }  
           chars[j] = original;  
         }  
       }  
       tree.add(new_layer);  
     }  
       
     // dfs to construct paths  
     Stack<String> path = new Stack<String>();  
     path.push(start);  
     dfs(start, end, 1, path, tree, rslt);  
       
     return rslt;  
   }  
     
   private void dfs(String s, String end, int index, Stack<String> path, List<Set<String>> tree, List<List<String>> rslt){  
     if(s.equals(end)){  
       List<String> validPath = new ArrayList<String>();  
       validPath.addAll(path);  
       rslt.add(validPath);  
       return;  
     }  
     if(index >= tree.size()) return;  
     Set<String> set = tree.get(index);  
     char[] chars = s.toCharArray();  
     for(int j = 0;j < s.length();j++){  
       char original = chars[j];  
       for(char c = 'a';c <= 'z';c++){  
         chars[j] = c;  
         String t = new String(chars);  
         if(set.contains(t)){  
           path.push(t);  
           dfs(t, end, index+1, path, tree, rslt);  
           path.pop();  
         }  
       }  
       chars[j] = original;  
     }  
   }  
 }  

Also, it is after several observation of others' code, I found that when listing the neighbors of a particular word, first convert it to char[] array and then replace a char instead of doing s.substring(0,i)+c+s.substring(i+1,s.length()) will save much time. It is probably because doing substring is initializing a new String each time, which is costly.

After seeing this post https://oj.leetcode.com/discuss/21902/java-solution-with-iteration on Discuss, I realized that DFS is not necessary. If I do a level-order traversal, delete the whole level from dict before traversal next level, I can efficiently deal with the case where a word is shared by to paths. Because a word shared by two paths must be at same location.

I give it a second trial using only BFS. And the code get accepted in 1000+ms.

 public class Solution {  
   public List<List<String>> findLadders(String start, String end, Set<String> dict) {  
     List<List<String>> rslt = new ArrayList<List<String>>();  
     Deque<List<String>> paths = new LinkedList<List<String>>();  
     boolean found = false;  
       
     // initialize path  
     List<String> path = new ArrayList<String>();  
     path.add(start);  
     paths.offerLast(path);  
       
     // add end to dictionary, remove start from dict  
     dict.add(end);  
     if(dict.contains(start)) dict.remove(start);  
       
     // BFS  
     while(!found && !paths.isEmpty()){  
       Set<String> set = new HashSet<String>();  
       int k = paths.size();  
       for(int i = 0;i < k;i++){  
         List<String> list = paths.pollFirst();  
         String s = list.get(list.size()-1);  
         for(String t : neighbors(s, dict)){  
           set.add(t);  
           List<String> newList = new ArrayList<String>(list);  
           newList.add(t);  
           paths.offerLast(newList);  
           if(t.equals(end)){  
             found = true;  
             rslt.add(newList);  
           }  
         }  
       }  
       dict.removeAll(set);  
     }  
     return rslt;  
   }  
     
   private List<String> neighbors(String s, Set<String> dict){  
     List<String> list = new ArrayList<String>();  
     char[] chars = s.toCharArray();  
     for(int j = 0;j < s.length();j++){  
       char original = chars[j];  
       for(char c = 'a';c <= 'z';c++){  
         chars[j] = c;  
         String t = new String(chars);  
         if(dict.contains(t)) list.add(t);  
       }  
       chars[j] = original;  
     }  
     return list;  
   }  
 }  

Improved Way: That is not enough. The highest run time distribution is around 700ms. I am far away from that yet. I looked into several posts about Word Ladder II. This two I found most helpful.
https://oj.leetcode.com/discuss/25970/java-modified-bfs-to-find-end-followed-dfs-reconstruct-paths
and http://yucoding.blogspot.com/2014/01/leetcode-question-word-ladder-ii.html (C++).

I found that the common point of their methods is to store the parents for each string instead of what is more straightforward, the children of each string. This reason for doing this is probably because storing the parents is using a lot less space than storing the children. (I tried storing children instead f parents, got MLE). Just Considering each word could have length * 26 at most children, while it will always have less than length*26 parents, since its neighbors selected  by its parent cannot become its parent.

To put it simple, parent selects children, only when two parents are alike, they can select same children. Thus, given each word, the size of its parents is much more less than the size of its children.

The following code applies mapping a word to its parents and got accept in 600+ ms. I keep the finding neighbor function and adding a dfs to find paths based on the parent relationship map.

 public class Solution {  
   public List<List<String>> findLadders(String start, String end, Set<String> dict) {  
     List<List<String>> rslt = new ArrayList<List<String>>();  
     Map<String, List<String>> parents = new HashMap<String, List<String>>();  
     boolean found = false;  
       
     // initialize  
     Set<String> cur_layer = new HashSet<String>();  
     cur_layer.add(start);  
     if(dict.contains(start)) dict.remove(start);  
     dict.add(end);  
       
     // BFS construct map  
     while(!found && !cur_layer.isEmpty()){  
       Set<String> new_layer = new HashSet<String>();  
       Iterator<String> iter = cur_layer.iterator();  
       while(iter.hasNext()){  
         String s = iter.next();  
         for(String t: neighbors(s, dict)){  
              new_layer.add(t);  
             if(!parents.containsKey(t)){  
               List<String> list = new ArrayList<String>();  
               list.add(s);  
               parents.put(t,list);  
             }else{  
               List<String> list = parents.get(t);  
               list.add(s);  
             }  
             if(t.equals(end)) found = true;  
         }  
       }  
       dict.removeAll(new_layer);  
       cur_layer = new_layer;  
     }  
       
     // DFS construct paths  
     Stack<String> path = new Stack<String>();  
     path.push(end);  
     dfs(start, end, path, parents, rslt);  
       
     return rslt;  
   }  
     
   private void dfs(String start, String s, Stack<String> path, Map<String, List<String>> parents, List<List<String>> rslt){  
        // base case  
     if(s.equals(start)){  
       List<String> list = new ArrayList<String>();  
       list.addAll(path);  
       Collections.reverse(list);  
       rslt.add(list);  
       return;  
     }  
     // edge case  
        if(!parents.containsKey(s)) return;  
     // recursion  
     for(String t: parents.get(s)){  
       path.push(t);  
       dfs(start, t, path, parents, rslt);  
       path.pop();  
     }  
   }  
     
   private List<String> neighbors(String s, Set<String> dict){   
     List<String> list = new ArrayList<String>();   
     char[] chars = s.toCharArray();   
     for(int j = 0;j < s.length();j++){   
       char original = chars[j];   
       for(char c = 'a';c <= 'z';c++){   
         chars[j] = c;   
         String t = new String(chars);   
         if(!t.equals(s) && dict.contains(t)) list.add(t);   
       }   
       chars[j] = original;   
     }   
     return list;   
   }   
 }