Thursday, October 31, 2013

Substring with Concatenation of All Words [Leetcode]

You are given a string, S, and a list of words, L, that are all of the same length. Find all starting indices of substring(s) S' in S that is a concatenation of each word in L exactly once and without any intervening characters.
For example, given:
S: "barfoothefoobarman"
L: ["foo", "bar"]
You should return the indices: [0,9].
(order does not matter).

Solution: the following implementation is nothing but brute force. To reduce the time complexity of searching, the hash table is used. Please use hash table properly, otherwise you will get TLE. Anyway, the time complexity is O(mn), where m and n are the lengths of S and L respectively. I believe there may be an optimal solution; maybe KMP like algorithm; not sure so far.

class Solution {
public:
    vector<int> findSubstring(string S, vector<string> &L) {
        // IMPORTANT: Please reset any member data you declared, as
        // the same Solution instance will be reused for each test case
        vector<int> ret;
        if(S.empty() || L.empty() || S.size()<L.size()*L[0].size())
            return ret;
        int wordLength = L[0].size(), sumLength=L.size()*wordLength;
        //Initilize hashtable: needFound[L[i]] is no. of occurences of L[i] in L
        unordered_map<string, int> needFound;
        for(int i=0; i<L.size(); ++i) needFound[L[i]]++;
        //Check one by one
        for(int i=0; i<=S.size()-sumLength; ++i){
            unordered_map<string, int> hasFound;
            int j = 0;
            while(j<sumLength){
                string cur = S.substr(i+j, wordLength);
                if(needFound.find(cur)!=needFound.end() && hasFound[cur]<needFound[cur])
                    hasFound[cur]++;
                else
                    break;
                j+=wordLength;
            }
            if(j==sumLength)
                ret.push_back(i);
        }
        return ret;
    }
};

I finally get a linear algorithm in terms of the length of S. To explain this solution, we define a valid window as an substring of S, say S[start:end] such that the frequency of each word in this substring is not larger than that in L. It is to be noted that if the frequency of each word in this subtring and in L is the same, we get the S', i.e., the substring in S that is a concatenation of each word in L exactly once.

To cover all possibilities, we check concatenation at 0, k, 2k, ..., (n-1)*k; then 1, k+1, 2k+1, ..., (n-1)*k+1; then 2, k+2, 2k+2, ..., (n-1)*k+2, etc.; until k-1, 2k-1, 3k-1, ..., (n-1)*k+k-1, where k is the length of each word (note: all the words have the same length), and n is the number of words. More details see comments in the following code. [Will explain more when I have time. :-)]

class Solution {
public:
    vector<int> findSubstring(string S, vector<string> &L) {
        // IMPORTANT: Please reset any member data you declared, as
        // the same Solution instance will be reused for each test case
        vector<int> ret;
        if(S.empty() || L.empty() || S.size()<L.size()*L[0].size()) 
            return ret;
        int wordLength = L[0].size(), sumLength=L.size()*wordLength;
        //Initilize hashtable: needFound[L[i]] is no. of occurences of L[i] in L
        unordered_map<string, int> needFound;
        for(int i=0; i<L.size(); ++i) needFound[L[i]]++;
        for(int i=0; i<wordLength; ++i){
            unordered_map<string, int> hasFound;
            int count=0;
            for(int start=i, end=i; end<=S.size()-wordLength; end+=wordLength){
                string cur = S.substr(end, wordLength);
                //the current segment is not found in L; the starting index of 
                //substring s' cound not be beween start and end, so skip to 'end'
                if(needFound.find(cur)==needFound.end()){
                    start=end+wordLength;
                    hasFound.clear();
                    count=0;
                    continue;
                }
                hasFound[cur]++;
                if(hasFound[cur]>needFound[cur]){ //the current window is not valid
                    //shrink the window to make it valid
                    while(S.substr(start, wordLength)!=cur){
                        count--;
                        hasFound[S.substr(start, wordLength)]--;
                        start+=wordLength;
                    }
                    hasFound[S.substr(start, wordLength)]--;
                    start+=wordLength;
                }
                else{ //the current winodw is valid
                    count++;
                    if(count==L.size()){ //found one
                        ret.push_back(start);
                        //shift the window to right one step
                        //but we keep the information we obtained so far
                        count--;
                        hasFound[S.substr(start, wordLength)]--;
                        start+=wordLength;
                    }
                }
            }
        }
        return ret;
    }
};

Linked List Cycle II [Leetcode]

Given a linked list, return the node where the cycle begins. If there is no cycle, return null.
Follow up:
Can you solve it without using extra space?

Solution: this is a follow-up question of Linked List Cycle; first find out the meeting point by advancing fast pointer 2 steps a time and advancing slow pointer 1 step a time; It is to be noted that the distance between the meeting point and the node where the cycle begins is the same as the distance between the head of the linked list and the node where the cycle begins. Thus, after finding out the meeting point, the only thing we need to do is to move the fast point to the head, and advance both slow and fast points simultaneously 1 step a time; when those two pointers meet again, we get the node where the cycle begins.
//k+pn+x=m; 2m-m=qn; =>k+x=(q-p)n

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     ListNode *next;
 *     ListNode(int x) : val(x), next(NULL) {}
 * };
 */
class Solution {
public:
    ListNode *detectCycle(ListNode *head) {
        // IMPORTANT: Please reset any member data you declared, as
        // the same Solution instance will be reused for each test case.
        if(!head || !head->next || !head->next->next)
            return NULL;
        ListNode* slow = head->next;
        ListNode* fast = head->next->next;
        while(slow!=fast){
            if(!fast->next || !fast->next->next)
                return NULL;
            slow=slow->next;
            fast=fast->next->next;
        }
        fast = head;
        while(slow!=fast){
            slow=slow->next;
            fast=fast->next;
        }
        return slow;
    }
};

Wednesday, October 30, 2013

Scramble String [Leetcode]

Given a string s1, we may represent it as a binary tree by partitioning it to two non-empty substrings recursively.
Below is one possible representation of s1 = "great":
    great
   /    \
  gr    eat
 / \    /  \
g   r  e   at
           / \
          a   t
To scramble the string, we may choose any non-leaf node and swap its two children.
For example, if we choose the node "gr" and swap its two children, it produces a scrambled string "rgeat".
    rgeat
   /    \
  rg    eat
 / \    /  \
r   g  e   at
           / \
          a   t
We say that "rgeat" is a scrambled string of "great".
Similarly, if we continue to swap the children of nodes "eat" and "at", it produces a scrambled string "rgtae".
    rgtae
   /    \
  rg    tae
 / \    /  \
r   g  ta  e
       / \
      t   a
We say that "rgtae" is a scrambled string of "great".
Given two strings s1 and s2 of the same length, determine if s2 is a scrambled string of s1.

Solution: if s1 is a scrambled string of s2, then (a) they must contain the same chars, and (b) you must can find out some point i to break s1 into two parts s1_1 and s1_2, and some point j to break s2 into two parts s2_1 and s2_2 such that one part of s1 is a scrambled string of one part of s2, and the rest part of s1 is also a scrambled string of another part of s2. My first thought is recursion, and it works well actually. Interestingly enough, some guy found this problem is a 3d DP problem [here is one feasible implementation: f(i,j,l)=(f(i,j,k) AND f(i+k,j+k,l-k)) OR (f(i,j+l-k,k) AND f(i+k,j,l-k)) where f(i,j,l) is true iff substring starts at s1[i] and substring starts at s2[j] both with length l are scrambled], but you may find both of these two methods are essentially the same. The only difference is that the recursion method does top-down while the DP method does bottom-up.

   /*******************************SOLUTION 1***********************************/
class Solution {
public:
    bool isScramble(string s1, string s2) {
        // Note: The Solution object is instantiated only once and is reused by each test case.
        if(s1==s2)
            return true;
        if(s1.size()!=s2.size())
            return false;
        string s1Copy = s1, s2Copy = s2;
        sort(s1Copy.begin(), s1Copy.end());
        sort(s2Copy.begin(), s2Copy.end());
        if(s1Copy!=s2Copy)
            return false;
        int n = s1.size();
        for(int i=1; i<n; ++i){
            //check (s1_1, s2_1) and (s1_2, s2_2)
            if(isScramble(s1.substr(0, i), s2.substr(0, i)) && isScramble(s1.substr(i), s2.substr(i)))
                return true;
            //check (s1_1, s2_2) and (s1_2, s2_1)
            if(isScramble(s1.substr(0, i), s2.substr(n-i)) && isScramble(s1.substr(i), s2.substr(0, n-i)))
                return true;
        }
        return false;
    }
};
   /*******************************SOLUTION 2***********************************/
class Solution {
public:
    bool isScramble(string s1, string s2) {
        if(s1==s2)
            return true;
        if(s1.size()!=s2.size())
            return false;
        string s1Copy = s1, s2Copy = s2;
        sort(s1Copy.begin(), s1Copy.end());
        sort(s2Copy.begin(), s2Copy.end());
        if(s1Copy!=s2Copy)
            return false;
        int n = s1.size();
        
        bool cache[n][n][n+1];
        for(int i=0; i<n; ++i)
            for(int j=0; j<n; ++j){
                cache[i][j][0]=true;
                cache[i][j][1]=(s1[i]==s2[j]);
            }
        for(int l=2; l<=n; ++l)
            for(int i=0; i<=n-l; ++i)
                for(int j=0; j<=n-l; ++j){
                    cache[i][j][l]=false;
                    for(int k=1; k<=l; ++k){
                        if(cache[i][j][k] && cache[i+k][j+k][l-k] 
                           || cache[i][j+l-k][k] && cache[i+k][j][l-k]){
                           cache[i][j][l]=true;
                           break;
                        }
                    }
                }
        return cache[0][0][n];
    }
}; 

Next Greater Element [GeeksforGeeks]

Given an array, print the Next Greater Element (NGE) for every element. The Next greater Element for an element x is the first greater element on the right side of x in array. Elements for which no greater element exist, consider itself as it next greater element.

Solution: the straightforward way is brute force, i.e, find the NGE for each element A[i] by scanning A[i+1:]. The time and space complexities of this method are O(N^2) and O(1) respectively. The more elegant way is to employ a stack. The basic idea is processing each element A[i] one by one by following the rules below:
  1. If the stack is empty, simply push index i into stack;
  2. If not, compare A[i] and the element of A with index of top-most element, say s,  of the stack, i.e., A[s]: 
    • If A[s] \ge A[i], simply push i into stack;
    • If A[s]<A[i], then it means that A[i] is NGE of A[s], thus we pop s, and repeatedly process the rest elements of stack until we got A[s] \ge A[i] or stack become empty; then we push i into stack. 
Since each element is pushed into stack exactly once, thus the time complexity is O(n). The space complexity is also O(n).

#include<iostream>
#include<stack>
#include<vector>
#include<ctime> //time()
#include<cstdlib> //srand() and rand()

std::vector<int> nextLargerEle(std::vector<int> arr){
    if(arr.size()<=1)
        return arr;
    std::vector<int> ret(arr.size(), 0);
    std::stack<int> s;
    for(size_t i=0; i<arr.size(); ++i){
        while(!s.empty()&&arr[s.top()]<arr[i]){
            ret[s.top()]=arr[i];
            s.pop();
        }
        s.push(i);
    }
    while(!s.empty()){
        ret[s.top()]=arr[s.top()];
        s.pop();
    }
    return ret;
}

int main(){
    std::vector<int> ret;
    ret.reserve(10);
    srand(time(NULL));
    for(int i=0; i<10; ++i)
        ret.push_back(rand()%9);
       
    std::cout<<"Input: ";
    for(size_t i=0; i<ret.size(); ++i)
        std::cout << ret[i] <<" ";
    std::cout<< std::endl;
    ret = nextLargerEle(ret);
   
    std::cout<<"Output: ";
    for(size_t i=0; i<ret.size(); ++i)
        std::cout << ret[i] <<" ";
    std::cout<< std::endl;
    return 0;
}

Minimum Window Substring [Leetcode]

Given a string S and a string T, find the minimum window in S which will contain all the characters in T in complexity O(n).
For example,
S = "ADOBECODEBANC"
T = "ABC"

Minimum window is "BANC".
Note:
If there is no such window in S that covers all characters in T, return the emtpy string "".
If there are multiple such windows, you are guaranteed that there will always be only one unique minimum window in S.

Solution: Two pointers [start, end] are needed to maintain the valid window (i.e., the window that covers all characters in T). One hash table needFound is used to track the number of each char in T we need to find; another hash table hasFound is used to track the number of each char in T we have already found. If hasFound[T[i]] \ge needFound[T[i]] for each char i in T, we know we have already found a valid window. We scan the string of S forward one char by one char and shrink the window if necessary.  Here is the detailed explanation of the solution.

class Solution {
public:
    string minWindow(string S, string T) {
        // Note: The Solution object is instantiated only once and is reused by each test case.
        string ret;
        if(S.empty()||T.empty()||S.size()<T.size()) 
            return ret;

        //Initilize needFound and hasFound
        unordered_map<char, int> needFound, hasFound;
        for(int i=0; i<T.size(); ++i){
            if(needFound.find(T[i])==needFound.end()){
                needFound[T[i]]=1; hasFound[T[i]]=0;
            }
            else
                needFound[T[i]]++;
        }
        int minLength = INT_MAX, minBegin = 0;
        int count = 0;
        for(int start=0, end=0; end<S.size(); ++end){
            if(needFound.find(S[end])==needFound.end())
                continue;
            hasFound[S[end]]++;
            if(hasFound[S[end]]<=needFound[S[end]])
                count++;
            if(count==T.size()){
                while(needFound.find(S[start])==needFound.end() || hasFound[S[start]]>needFound[S[start]]){
                    if (needFound.find(S[start])!=needFound.end() && hasFound[S[start]]>needFound[S[start]])
                        hasFound[S[start]]--;
                    start++;
                }
                if(end-start+1<minLength){
                    minBegin = start;
                    minLength = end-start+1;
                }
            }
        }
        if(count==T.size())
            ret = S.substr(minBegin, minLength);
        return ret;
    }
}; 

Tuesday, October 29, 2013

Recover Binary Search Tree [Leetcode]

Two elements of a binary search tree (BST) are swapped by mistake.
Recover the tree without changing its structure.
Note:
A solution using O(n) space is pretty straight forward. Could you devise a constant space solution?
confused what "{1,#,2,3}" means? > read more on how binary tree is serialized on OJ.

OJ's Binary Tree Serialization: The serialization of a binary tree follows a level order traversal, where '#' signifies a path terminator where no node exists below.
Here's an example:

   1
  / \
 2   3
    /
   4
    \
     5
The above binary tree is serialized as "{1,2,3,#,#,4,#,#,5}".

Solution: we solve this problem by doing In-order traversal. Assume the In-order traversal of a given tree without two swapped nodes by mistake is 1, 2, 3, 4, 5, 5, 6, 6, 6, 7, 7, 7, 8, 9. We has three probabilities:
  1. If there is no two swapped nodes by mistake, there is no inversions; 
  2. If a node with value a is swapped with the next first node with value b>a in the In-order traversal (e.g. the first 5 and the first 6), we get one inversion; 
  3. Otherwise we get two inversion (e.g. the first 5 and the first 7). 
For case 1, we do not need to anything; For case 2, we simply exchange the values of two nodes involving in the inversion; For case 3, we need to exchange the first node in the first inversion and the second node in the second inversion. We note that the pointer pre is used to track the last visited node before the current node.

/**
 * Definition for binary tree
 * struct TreeNode {
 *     int val;
 *     TreeNode *left;
 *     TreeNode *right;
 *     TreeNode(int x) : val(x), left(NULL), right(NULL) {}
 * };
 */
class Solution {
public:
    void recoverTreeHelper(TreeNode *root, TreeNode* &pre, TreeNode* &fst, TreeNode* &snd){
        if(!root) return;
        recoverTreeHelper(root->left, pre, fst, snd);
        if(pre!=NULL && pre->val>root->val){
            if(fst==NULL){
                fst=pre;
                snd=root;
            }
            else
                snd=root;
        }
        pre=root;
        recoverTreeHelper(root->right, pre, fst, snd);
    }
    void recoverTree(TreeNode *root) {
        // Start typing your C/C++ solution below
        // DO NOT write int main() function
        TreeNode *fst=NULL;
        TreeNode *snd=NULL;
        TreeNode *pre=NULL;
        recoverTreeHelper(root, pre, fst, snd);
        if(fst==NULL)
            return;
        int tmp = fst->val;
        fst->val = snd->val;
        snd->val = tmp;
    }
};



Linked List Cycle [Leetcode]

Given a linked list, determine if it has a cycle in it.
Follow up:
Can you solve it without using extra space?

Solution: classic interview question, which can be solve by slow-fast pointers. The speed of fast pointer is twice of speed of slow one. If a cycle exists, two pointers would meet somewhere. [here is a follow-up question]

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     ListNode *next;
 *     ListNode(int x) : val(x), next(NULL) {}
 * };
 */
class Solution {
public:
    bool hasCycle(ListNode *head) {
        // IMPORTANT: Please reset any member data you declared, as
        // the same Solution instance will be reused for each test case.
        if(!head || !head->next || !head->next->next)
            return false;
        ListNode* slow = head->next;
        ListNode* fast = head->next->next;
        while(slow!=fast){
            if(!fast->next || !fast->next->next)
                return false;
            slow=slow->next;
            fast=fast->next->next;
        }
        return true;
    }
};

Word Search [Leetcode]

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.
Solution: DFS; we use a visited matrix to record the visited nodes.

class Solution {
public:
    bool searcher(vector<vector<char>> board, string word, int cur, vector<vector<bool>> &visited, int x, int y){
        if(board[x][y]!=word[cur])
            return false;
        if(cur==word.size()-1)
            return true;
        visited[x][y]=true;
        int m=board.size(), n=board[0].size();
        if(y-1>=0 && !visited[x][y-1] && searcher(board, word, cur+1, visited, x, y-1))
            return true;
        if(y+1<n && !visited[x][y+1] && searcher(board, word, cur+1, visited, x, y+1))
            return true;
        if(x-1>=0 && !visited[x-1][y] && searcher(board, word, cur+1, visited, x-1, y))
            return true;
        if(x+1<m && !visited[x+1][y] && searcher(board, word, cur+1, visited, x+1, y))
            return true;
        visited[x][y]=false;
        return false;
    }
    bool exist(vector<vector<char> > &board, string word) {
        // Note: The Solution object is instantiated only once and is reused by each test case.
        if(word.empty())
            return true;
        if(board.empty() || board[0].empty())
            return false;
        int m = board.size(), n = board[0].size();
        vector<vector<bool>> visited(m, vector<bool>(n, false));
        for(int i=0; i<m; ++i){
            for(int j=0; j<n; ++j){
                if(board[i][j]==word[0]){
                    if(searcher(board, word, 0, visited, i, j))
                        return true;
                }
            }
        }
        return false;
    }
};

Monday, October 28, 2013

Simplify Path [Leetcode]

Given an absolute path for a file (Unix-style), simplify it.
For example,
path = "/home/", => "/home"
path = "/a/./b/../../c/", => "/c"

Corner Cases:
  • Did you consider the case where path = "/../"?
    In this case, you should return "/".
  • Another corner case is the path might contain multiple slashes '/' together, such as "/home//foo/".
    In this case, you should ignore redundant slashes and return "/home/foo".
Solution: the rules are as follows: 1. skip multiple '/'s; 2. when '.' is encountered, stay in the current folder; 3. when '..' is encountered, go back to the previous folder; 4. otherwise, go to the next level of folder.

class Solution {
public:
    string simplifyPath(string path) {
        // Start typing your C/C++ solution below
        // DO NOT write int main() function
        string ret;
        //invalid input check
        if(path.empty()||path[0]!='/')
            return ret;
        vector<string> p; //record the generated path until now
        int i=1;
        while(i<path.size()){
            //skip '/'
            if(path[i]=='/'){
                ++i;
                continue;
            }
            //collect the substring btwn two '/'
            string cur;
            while(path[i]!='/' && i<path.size()){
                cur+=path[i];
                i++;
            }
            if(cur==".") //stay in the current folder
                continue;
            else if(cur==".."){ //go back to previous folder
                if(!p.empty())
                    p.pop_back();
            }
            else // go further to the next level folder
                p.push_back(cur);
        }
        if(p.empty())
            return "/";
        //construct the path
        for(int i=0; i<p.size(); ++i){
            ret+="/";
            ret+=p[i];
        }
        return ret;
    }
};



Sunday, October 27, 2013

Extract Leaves of a Binary Tree in a Doubly Linked List

Given a Binary Tree, extract all leaves of it in a Doubly Linked List (DLL). Note that the DLL need to be created in-place. Assume that the node structure of DLL and Binary Tree is same, only the meaning of left and right pointers are different. In DLL, left means previous pointer and right means next pointer [Check here for the details].
Let the following be input binary tree
        1
     /     \
    2       3
   / \       \
  4   5       6
 / \         / \
7   8       9   10 
Output:
Doubly Linked List
7<->8<->5<->9<->10

Modified Tree:
        1
     /     \
    2       3
   /         \
  4           6
Solution: we travel the tree in the order of right subtree, current node, and left subtree; thus, the order of leaves we travel are 10, 9, 5, 8, 7. Each time a leaf is encountered, we add it to the beginning of the DLL (this is also why we travel the tree in the order of right subtree, current node, and left subtree).

#include<iostream>

using std::cout;
using std::endl;

//Structure for tree and linked list 
typedef struct node{
    int data;
    struct node *left, *right;
}* Node;

//Create a node and return its addr
Node createNode(int data){
    Node newNode = new struct node;
    newNode->data = data;
    newNode->left = NULL;
    newNode->right = NULL;
    return newNode;
}

//Extract leaves of binary tree to a DLL
void extractLeafList(Node &root, Node &head){
    if(!root) return;
    if(!root->left && !root->right){
        if(head!=NULL){
            root->right = head;
            head->left = root;
        }
        head = root;
        root = NULL;
        return;
    }
    extractLeafList(root->right, head);
    extractLeafList(root->left, head);
}

//Print the binary tree in In-order
void printTree(Node root){
    if(!root) return;
    printTree(root->left);
    cout << root->data << " ";
    printTree(root->right);
}

//Print DLL 
void printDLL(Node head){
    while(head!=NULL){
        cout << head->data << " ";
        head = head->right;
    }
    cout<<endl;
}

int main(){
    //Create a binary tree
    Node head = NULL;
    Node root = createNode(1);
    root->left = createNode(2);
    root->right = createNode(3);
    root->left->left = createNode(4);
    root->left->right = createNode(5);
    root->right->right = createNode(6);
    root->left->left->left = createNode(7);
    root->left->left->right = createNode(8);
    root->right->right->left = createNode(9);
    root->right->right->right = createNode(10);

    cout<<"Inorder Trvaersal of given Tree is:"<<endl;
    printTree(root);
    cout<<endl;

    extractLeafList(root, head);

    cout<<"Extracted Double Linked list is:"<<endl;
    printDLL(head);

    cout<<"Inorder traversal of modified tree is:"<<endl;
    printTree(root);
    cout<<endl;
    return 0;
}

Saturday, October 26, 2013

Detect Cycle in an Undirected Graph

Given an undirected graph, check if the graph contains cycle(s) or not. The detailed description of the problem can be found here.

Solution: DFS is employed to solve this problem. Detecting cycles in the undirected graph is much simpler than that in the directed graph. Consider walking from node i to node j. If j was visited before and is not the parent of node i, we say we get a cycle.  The following is the solution to this problem; the time complexity if O(V+E) [part of code is from geeksforgeeks].

// A C++ Program to detect cycle in an undirected graph
#include<iostream>
#include <vector>

using std::vector;
using std::cout;
using std::endl;

class Graph
{
private:
    int V; // No. of vertices
    vector<int> *adj;
    bool isCyclicUtil(int v, vector<bool> &visited, int parent);
public:
    Graph(int v):V(v), adj(new vector<int>[V]){}
    void addEdge(int v, int w){
        adj[v].push_back(w);
        adj[w].push_back(v);
    }
    bool isCyclic();
};

bool Graph::isCyclicUtil(int v, vector<bool> &visited, int parent){
    visited[v] = true;
    for (size_t i=0; i<adj[v].size(); ++i){
        int nbr = adj[v][i];
        if (!visited[nbr]){
            cout << "path: " << v << "----->" << nbr << endl;   
            if(isCyclicUtil(nbr, visited, v))
                return true;
        }
        else if(nbr != parent){
            cout << "path: " << v << "----->" << nbr << endl;   
            cout << "back edge: " << v << "----->" << nbr << endl;
            return true;
        }
    }
    return false;
}

bool Graph::isCyclic(){
    vector<bool> visited(V, false);
    for (int i=0; i<V; ++i)
        if (!visited[i] && isCyclicUtil(i, visited, -1))
            return true;

    return false;
}

int main()
{
    // Create a graph given in the above diagram
    Graph g(5);
    g.addEdge(1, 0);
    g.addEdge(0, 2);
    g.addEdge(0, 3);
    g.addEdge(3, 4);
    g.addEdge(0, 4);

    if(g.isCyclic())
        cout << "Graph contains cycle" << endl;
    else
        cout << "Graph doesn't contain cycle" << endl;
    return 0;
}

Detect Cycle in a Directed Graph

Given a directed graph, check if the graph contains cycle(s) or not. The detailed description of the problem can be found here.

Solution: DFS is employed to solve this problem. It is to be noted that it does not mean we get a cycle when a visited node is encountered again. To detect the cycles, we need to use a container to record all the nodes we've visited along the current path. If the next node we are visiting has already been visited and recorded in the container, we say we get a cycle. The following is the solution to this problem; the time complexity if O(V+E) [part of code is from geeksforgeeks]

// A C++ Program to detect cycle in a directed graph
#include<iostream>
#include <vector>
 
using std::vector;
using std::cout;
using std::endl;
 
class Graph
{
private:
    int V;  // No. of vertices
    vector<int> *adj;
    bool isCyclicUtil(int v, vector<bool> &visited, vector<bool> rs);
public:
    Graph(int v):V(v), adj(new vector<int>[V]){}
    void addEdge(int v, int w){ adj[v].push_back(w);}
    bool isCyclic();
};
 
bool Graph::isCyclicUtil(int v, vector<bool> &visited, vector<bool> recStack){
    visited[v]=true;
    recStack[v]=true;
    for(size_t i=0; i<adj[v].size(); ++i){
        int nbr = adj[v][i];
        if(!visited[nbr]){
            cout << "path: " << v << "----->" << nbr << endl;    
            if(isCyclicUtil(nbr, visited, recStack))
                return true;
        }
        else if(recStack[nbr]){
            cout << "path: " << v << "----->" << nbr << endl;    
            cout << "back edge: " << v << "----->" << nbr << endl;
            return true;
        }
    }
    return false;
}
 
bool Graph::isCyclic(){
    vector<bool> visited(V, false);
    vector<bool> recStack(V, false);
    for(int i=0; i<V; i++){
        if(!visited[i] && isCyclicUtil(i, visited, recStack))
        return true;
    }
    return false;
}
 
int main(){
    // Create a graph given in the above diagram
    Graph g(4);
    g.addEdge(0, 1);
    g.addEdge(0, 2);
    g.addEdge(1, 2);
    g.addEdge(2, 3);
    g.addEdge(2, 0);
    g.addEdge(3, 3);
 
    if(g.isCyclic())
        cout << "Graph contains cycle" << endl;
    else
        cout << "Graph doesn't contain cycle" << endl;
    return 0;
}

Friday, October 25, 2013

Maximal Rectangle [Leetcode]

Given a 2D binary matrix filled with 0's and 1's, find the largest rectangle containing all ones and return its area.

Solution: the best solution to this problem I have ever seen is O(mn), which based on the solution to Largest Rectangle in Histogram [see here for explanation, the first algorithm in this post is O(m^2n^2)].

   /*******************************SOLUTION 1***********************************/
class Solution {
public:
    int largestRectangleArea(vector<int> &height) {
        if(height.empty()) return 0;
        //adding a dummy bar with height -1.
        height.push_back(-1);
        int maxSum = 0, n=height.size();
        stack<int> s;
        for(int i=0; i<n; ++i){
            while(!s.empty() && height[i]<height[s.top()]){
                int curIndex = s.top();
                s.pop();
                int numBar = (s.empty()?i:i-s.top()-1);
                maxSum = max(maxSum, height[curIndex]*numBar);
            }
            s.push(i);
        }
        return maxSum;
    }
    
    int maximalRectangle(vector<vector<char> > &matrix) {
        // Note: The Solution object is instantiated only once and is reused by each test case.
        if(matrix.empty() || matrix[0].empty())
            return 0;
        int rows = matrix.size();
        int cols = matrix[0].size();
        vector<int> heights(cols, 0);
        int maxArea = 0;
        for(int i=0; i<rows; ++i){
            for(int j=0; j<cols; ++j){
                if(i==0)
                    heights[j]=matrix[0][j]-'0';
                else
                    heights[j] = (matrix[i][j]=='0'?0:heights[j]+1);
            }
            maxArea = max(maxArea, largestRectangleArea(heights));
        }
        return maxArea;
    }
};
   /*******************************SOLUTION 2***********************************/
The time complexity of the following implementation is O(mn^2) and the space requirement is O(mn) [see here for explanation]. If you want a constant space algorithm, you can just delete the two-dimensional heights array, and compute it dynamically, thus the time complexity becomes O(m^2n^2). 

class Solution {
public:
    int maximalRectangle(vector<vector<char> > &matrix) {
        // Note: The Solution object is instantiated only once and is reused by each test case.
        if(matrix.empty() || matrix[0].empty())
            return 0;
        int rows = matrix.size();
        int cols = matrix[0].size();
        vector<vector<int>> heights(rows, vector<int>(cols, 0)); //along the columns
        for(int i=0; i<rows; ++i){
            for(int j=0; j<cols; ++j){
                if(i==0)
                    heights[0][j]=matrix[0][j]-'0';
                else
                    heights[i][j] = (matrix[i][j]=='0'?0:heights[i-1][j]+1);
            }
        }
        int maxArea = 0;
        for(int i=0; i<rows; ++i){
            for(int j=0; j<cols; ++j){
                if(matrix[i][j]=='0')
                    continue;
                int h = INT_MAX;
                int area = 0;
                for(int k=j; k<cols && matrix[i][k]=='1'; ++k){
                    h = min(h, heights[i][k]);            
                    area = (k-j+1)*h;
                    maxArea = max(maxArea, area);
                }
            }
        }
        return maxArea;
    }
}; 

Sqrt(x) [Leetcode]

Implement int sqrt(int x).
Compute and return the square root of x.

Solution: binary search; pay attention to the overflow; we note that it is possible to get overflow when you compute the square of a large number or compute the average of two large numbers. For example, we have two integers a and b, if we compute the average of a and b by (a+b)/2, it is possible to get overflow, so the good way is to compute the average by a+(b-a)/2. Anyway, in this problem, we need to compute square of a large number, so here we use "long long" to avoid the overflow introducing by operating "int". [other good solutions: 1, 2]

class Solution {
public:
    int sqrt(int x) {
        // Note: The Solution object is instantiated only once and is reused by each test case.
        if(x<=0) return 0;
        if(x==1) return 1;
        long long start = 0, end = x;
        while(start<=end){
            //long long mid = (start+end)/2;
            long long mid = start+(end-start)/2;
            long long s = mid*mid;
            if(s==x)
                return mid;
            if(s<x)
                start = mid+1;
            else
                end = mid-1;
        }
        return start*start<=x?start:start-1; 
    }
};

Best Time to Buy and Sell Stock III [Leetcode]

Say you have an array for which the ith element is the price of a given stock on day i.
Design an algorithm to find the maximum profit. You may complete at most two transactions.
Note:
You may not engage in multiple transactions at the same time (ie, you must sell the stock before you buy again).

Solution: we divide the whole sequence into two parts at each i, find the maximum intervals or profits in both left and right parts, add both of them together to get the maximum profit on this division. Then we select the largest one. We employ DP to solve this problem [another good explanation can be found here].

class Solution {
public:
    int maxProfit(vector<int> &prices) {
        // Start typing your C/C++ solution below
        // DO NOT write int main() function
        if(prices.empty()) return 0;
        int n = prices.size();
        
        //compute the max profit until right now
        vector<int> historyProfit(n, 0);
        int curMin = prices[0];
        for(int i=1; i<n; i++){
            curMin = min(curMin, prices[i]);
            historyProfit[i] = max(historyProfit[i-1], prices[i]-curMin);
        }
        //compute the max profit from now on
        vector<int> futureProfit(n, 0);
        int curMax = prices[n-1];
        for(int i=n-2; i>=0; i--){
            curMax = max(curMax, prices[i]);
            futureProfit[i] = max(futureProfit[i+1], curMax-prices[i]);
        }
        //select the maximum overall profit
        int maxProfit = 0;
        for(int i=0; i<n; i++)
            maxProfit = max(maxProfit, historyProfit[i]+futureProfit[i]);
        return maxProfit;
    }
};

Best Time to Buy and Sell Stock II [Leetcode]

Say you have an array for which the ith element is the price of a given stock on day i.
Design an algorithm to find the maximum profit. You may complete as many transactions as you like (ie, buy one and sell one share of the stock multiple times). However, you may not engage in multiple transactions at the same time (ie, you must sell the stock before you buy again).

Solution: find out all increasing consecutive subsequence S's; buy stock at the beginning of S and sell it at the end of the S.

class Solution {
public:
    int maxProfit(vector<int> &prices) {
        // Start typing your C/C++ solution below
        // DO NOT write int main() function
        if(prices.empty()) return 0;
       
        int sumProfit = 0;
        int minCur = prices[0];
        for(int i=1; i<prices.size(); ++i){
            if(prices[i]<prices[i-1]){
                sumProfit+=(prices[i-1]-minCur);
                minCur = prices[i];
            }
        }
        if(prices.back()>minCur)
            sumProfit += prices.back()-minCur;
        return sumProfit;
    }
};

Best Time to Buy and Sell Stock [Leetcode]

Say you have an array for which the ith element is the price of a given stock on day i.
If you were only permitted to complete at most one transaction (ie, buy one and sell one share of the stock), design an algorithm to find the maximum profit.

Solution: DP, the objective is to find out the maximum interval (prices[i], prices[j]) where i<j.

class Solution {
public:
    int maxProfit(vector<int> &prices) {
        // Start typing your C/C++ solution below
        // DO NOT write int main() function
        if(prices.empty()) return 0;
        int maxProfit = 0;
        int curMin = prices[0];
        for(int i=1; i<prices.size(); ++i){
            curMin = min(curMin, prices[i]);
            maxProfit = max(maxProfit, prices[i]-curMin);
        }
        return maxProfit;
    }
};

Largest Rectangle in Histogram [Leetcode]

Given n non-negative integers representing the histogram's bar height where the width of each bar is 1, find the area of largest rectangle in the histogram.

Above is a histogram where width of each bar is 1, given height = [2,1,5,6,2,3].

The largest rectangle is shown in the shaded area, which has area = 10 unit.
For example,
Given height = [2,1,5,6,2,3],
return 10.

Solution: a straightforward way is brute force, which means for each element, we walks towards both sides and find the first one lower than current element in each side, then compute the area. we take bar 2 with height of 5 as an example. The first one lower than bar 2 in the left side is bar i=1; the first one lower than bar 2 in the right side is bar j=4. so the area is (j-i-1)*5, i.e., 10. In this way, it is easy to find out the maximum area. But here I do not want to give an implementation of this solution since there is a more elegant one as follows (I get it online).

Let's reconsider this problem, we want a linear time algorithm by scanning the entire array exactly one. When we want to compute the area of the largest rectangle with the height of height[i] (bar i), we need to know the position of the first element lower than bar i (counting from i towards beginning) in the left side; here we use a stack to remember this. Check here to get the complete algorithm. It is to be noted that the algorithm is linear since each bar is pushed and popped exactly once.
class Solution {
public:
    int largestRectangleArea(vector<int> &height) {
        // Note: The Solution object is instantiated only once and is reused by each test case.
        if(height.empty()) return 0;
        //adding a dummy bar with height -1.
        height.push_back(-1);
        int maxSum = 0, n=height.size();
        stack<int> s;
        for(int i=0; i<n; ++i){
            while(!s.empty() && height[i]<height[s.top()]){
                int curIndex = s.top();
                s.pop();
                int numBar = (s.empty()?i:i-s.top()-1);
                maxSum = max(maxSum, height[curIndex]*numBar);
            }
            s.push(i);
        }
        return maxSum;
    }
};

Thursday, October 24, 2013

4Sum [Leetcode]

Given an array S of n integers, are there elements a, b, c, and d in S such that a + b + c + d = target? Find all unique quadruplets in the array which gives the sum of target.
Note:
  • Elements in a quadruplet (a,b,c,d) must be in non-descending order. (ie, abcd)
  • The solution set must not contain duplicate quadruplets.
    For example, given array S = {1 0 -1 0 -2 2}, and target = 0.

    A solution set is:
    (-1,  0, 0, 1)
    (-2, -1, 1, 2)
    (-2,  0, 0, 2)

Solution: based on 2sum problem; remember to remove duplicates; time complexity is O(n^3) and space complexity is O(1). [we also can compute the sum of each pair of two elements and store them in a hashtable, this way we can get O(n^2) of time complexity, but the space complexity is increased to O(n^2)]

class Solution {
public:
    void twoSum(vector<int> num, int begin, int first, int second, int target, vector<vector<int> >& r){
        if(begin >= (num.size()-1)) return;
        int b = begin;
        int e = num.size()-1;
        while(b<e){
            if(num[b]+num[e]==target){
                vector<int> subR;
                subR.push_back(first);
                subR.push_back(second);
                subR.push_back(num[b]);
                subR.push_back(num[e]);
                r.push_back(subR);
                //remove duplicates
                do{++b;} while(b<e && num[b]==num[b-1]);
                do{--e;} while(b<e && num[e]==num[e+1]);
            }
            else if(num[b]+num[e]<target)
                ++b;
            else
                --e;
        }
    }
    vector<vector<int> > fourSum(vector<int> &num, int target) {
        // Start typing your C/C++ solution below
        // DO NOT write int main() function
        vector<vector<int>> ret;
        if(num.size()<4)
            return ret;
        sort(num.begin(), num.end());
        for(int i=0; i<num.size()-3; ++i){
            //remove duplicates
            if(i!=0 && num[i]==num[i-1])
                continue;
            for(int j=i+1; j<num.size()-2; j++){
                //remove duplicates
                if(j!=i+1 && num[j]==num[j-1])
                    continue;
                twoSum(num, j+1, num[i], num[j], target-(num[i]+num[j]), ret);
            }
        }
        return ret;        
    }
};

Edit Distance [Leetcode]

Given two words word1 and word2, find the minimum number of steps required to convert word1 to word2. (each operation is counted as 1 step.)
You have the following 3 operations permitted on a word:
a) Insert a character
b) Delete a character
c) Replace a character

Solution: this is a famous DP problem which is well-explained in MIT Open Course. One thing which should be noted is that we can use linear space to solve the problem instead of two-dimensional cache like previous question.

class Solution {
public:
    int minDistance(string word1, string word2) {
        // Note: The Solution object is instantiated only once and is reused by each test case.
        int m = word1.size();
        int n = word2.size();
        vector<vector<int>> cache(m+1, vector<int>(n+1,0));
        for(int i=0; i<=m; ++i){
            for(int j=0; j<=n; ++j){
                if(i==0 && j!=0)
                    cache[i][j]=j;
                if(i!=0 && j==0)
                    cache[i][j]=i;
                if(i!=0 && j!=0){
                    if(word1[i-1]==word2[j-1])
                        cache[i][j] = cache[i-1][j-1];
                    else
                        cache[i][j] = min(cache[i-1][j-1], min(cache[i-1][j], cache[i][j-1]))+1;
                }
            }
        }
        return cache[m][n];
    }
}; 

Interleaving String [Leetcode]

Given s1, s2, s3, find whether s3 is formed by the interleaving of s1 and s2.
For example,
Given:
s1 = "aabcc",
s2 = "dbbca",
When s3 = "aadbbcbcac", return true.
When s3 = "aadbbbaccc", return false.

Solution: DP again. Transitive function is: dp(i,j) = (s3[i+j]==s1[i] && dp(i-1,j)) OR (s3[i+j]==s2[j] && dp(i,j-1)), where dp(i,j) means s3[1,...,i+j] can be formed by interleaving s1[1,...,i] and s2[1,...,j]. (for the ease of description, we assume the strings use 1-based indexing).

class Solution {
public:
    bool isInterleave(string s1, string s2, string s3) {
        // Note: The Solution object is instantiated only once and is reused by each test case.   
        int l1 = s1.size(), l2=s2.size(), l3=s3.size();
        if(l1+l2!=l3) return false;
        if(l1==0) return s3==s2;
        if(l2==0) return s3==s1;
        vector<vector<int>> cache(l1+1, vector<int>(l2+1, 0));
       
        for(int i=0; i<=l1; ++i)
            for(int j=0; j<=l2; ++j){
                if(i==0 && j==0)
                    cache[i][j]=1;
                else if(i==0 && s3[j-1]==s2[j-1] && cache[i][j-1]){
                    cache[i][j]=cache[i][j-1];
                }
                else if(j==0 && s3[i-1]==s1[i-1] && cache[i-1][j]){
                    cache[i][j]=cache[i-1][j];
                }
                else
                    cache[i][j]= (s3[i+j-1]==s2[j-1] && cache[i][j-1]) || (s3[i+j-1]==s1[i-1] && cache[i-1][j]);
            }
        return cache.back().back();
    }
}; 

The following implementation is based on one-dimensional cache [the following code can be further optimized, but I do not want to do it for clarity].

class Solution {
public:
    bool isInterleave(string s1, string s2, string s3) {
        // Note: The Solution object is instantiated only once and is reused by each test case.    
        int l1 = s1.size(), l2=s2.size(), l3=s3.size();
        if(l1+l2!=l3) return false;
        if(l1==0) return s3==s2;
        if(l2==0) return s3==s1;
        vector<int> cache(l2+1, 0);
        
        for(int i=0; i<=l1; ++i)
            for(int j=0; j<=l2; ++j){
                if(i==0 && j==0)
                    cache[j]=1;
                else if(i==0 && s3[j-1]==s2[j-1] && cache[j-1]){
                    cache[j]=cache[j-1];
                }
                else if(j==0 && s3[i-1]==s1[i-1] && cache[j]){
                    cache[j]=cache[j];
                }
                else
                    cache[j]= (s3[i+j-1]==s2[j-1] && cache[j-1]) || (s3[i+j-1]==s1[i-1] && cache[j]);
            }
        return cache.back();
    }
};

Distinct Subsequences [Leetcode]

Given a string S and a string T, count the number of distinct subsequences of T in S.
A subsequence of a string is a new string which is formed from the original string by deleting some (can be none) of the characters without disturbing the relative positions of the remaining characters. (ie, "ACE" is a subsequence of "ABCDE" while "AEC" is not).
Here is an example:
S = "rabbbit", T = "rabbit"
Return 3.

Solution: this is a DP problem; our objective is to count the number of appearance of T in S where some char can be inserted into T.
Transitive function is: dp(i,j) = dp(i-1,j) + (S[i]==S[j]?dp(i-1,j-1):0), where dp(i,j) means the number of distinct subsequences of T[0,..., j] in S[0,...,i].

class Solution {
public:
    int numDistinct(string S, string T) {
        // Note: The Solution object is instantiated only once and is reused by each test case.
        int s = S.size();
        int t = T.size();
        if(s==0 || t==0 || s<t) return 0;
        vector<vector<int>> cache(s, vector<int>(t, 0));
        cache[0][0]= (S[0]==T[0]?1:0);
        for(int i=1; i<s; ++i){
            for(int j=0; j<=min(i, t-1); ++j){
                if(j==0)
                    cache[i][j] = cache[i-1][j]+(S[i]==T[j]?1:0);
                else
                    cache[i][j] = cache[i-1][j]+(S[i]==T[j]?cache[i-1][j-1]:0);
            }
        }
        return cache[s-1][t-1];

    }
};

It is to be noted that when we do i-th iteration, we only need the result in (i-1)-th iteration. So here we only need one-dimensional array of size t, where t is the length of T.  Consider the i-th iteration: at the beginning of the iteration: cache[j] is the number of distinct subsequences of T[0,..., j] in S[0,...,i-1]; while it is updated to the number of distinct subsequences of T[0,..., j] in S[0,...,i] in the end of i-th iteration.

class Solution {
public:
    int numDistinct(string S, string T) {
        // Note: The Solution object is instantiated only once and is reused by each test case.
        int s = S.size();
        int t = T.size();
        if(s==0 || t==0 || s<t) return 0;

        vector<int> cache(t, 0);
        for(int i=0; i<s; ++i)
            for(int j=min(i, t-1); j>=0; j--){
                if(S[i]==T[j])
                    cache[j] += (j==0?1:cache[j-1]);
            }
        return cache[t-1];
    }
};

Surrounded Regions [Leetcode]

Given a 2D board containing 'X' and 'O', capture all regions surrounded by 'X'.
A region is captured by flipping all 'O's into 'X's in that surrounded region .
For example,

X X X X
X O O X
X X O X
X O X X
After running your function, the board should be:
X X X X
X X X X
X X X X
X O X X

Solution: we search the elements with a way out from the out-most layer to inside, and mark such elements as 'N's; after search, we change all 'O' to 'X' and change all 'N' to 'O'; the search can be done in either BFS or DFS fashion.

   /*******************************SOLUTION 1***********************************/
class Solution {
public:
    typedef struct point{
      int x;
      int y;
      point(int a, int b){x=a; y=b;}
    } Point;
    void solve(vector<vector<char>> &board) {
        // Start typing your C/C++ solution below
        // DO NOT write int main() function
        if(board.empty() || board[0].empty())
            return;
        int rows = board.size();
        int cols = board[0].size();
        queue<Point> q;
        //scan the first and last rows
        for(int i=0; i<cols; ++i){
            if(board[0][i]=='O'){
                q.push(Point(0,i));
                board[0][i]='N';
            }
            if(board[rows-1][i]=='O'){
                q.push(Point(rows-1, i));
                board[rows-1][i]='N';
            }
        }
        //scan the first and last colomns
        for(int i=1; i<rows-1; ++i){
            if(board[i][0]=='O'){
                q.push(Point(i, 0));
                board[i][0]='N';
            }
            if(board[i][cols-1]=='O'){
                q.push(Point(i, cols-1));
                board[i][cols-1]='N';
            }
        }
        //BFS
        while(!q.empty()){
            Point tmp = q.front();
            q.pop();
            //check surrounding cells
            if(tmp.x-1>=0 && board[tmp.x-1][tmp.y]=='O'){
                q.push(Point(tmp.x-1, tmp.y));
                board[tmp.x-1][tmp.y]='N';
            }
            if(tmp.x+1<rows && board[tmp.x+1][tmp.y]=='O'){
                q.push(Point(tmp.x+1, tmp.y));
                board[tmp.x+1][tmp.y]='N';
            }
            if(tmp.y-1>=0 && board[tmp.x][tmp.y-1]=='O'){
                q.push(Point(tmp.x, tmp.y-1));
                board[tmp.x][tmp.y-1]='N';
            }
            if(tmp.y+1<cols && board[tmp.x][tmp.y+1]=='O'){
                q.push(Point(tmp.x, tmp.y+1));
                board[tmp.x][tmp.y+1]='N';
            }
        }
        //fill the board
        for(int i=0; i<rows; ++i)
            for(int j=0; j<cols; ++j){
                if(board[i][j]=='O')
                    board[i][j]='X';
                if(board[i][j]=='N')
                    board[i][j]='O';
            }
    }
};

   /*******************************SOLUTION 2***********************************/
class Solution {
public:
    void DFS(vector<vector<char>> &board, int x, int y){
        int rows = board.size();
        int cols = board[0].size();
        if(x-1>=0 && board[x-1][y]=='O'){
            board[x-1][y]='N';
            DFS(board, x-1, y);
        }
        if(x+1<rows && board[x+1][y]=='O'){
            board[x+1][y]='N';
            DFS(board, x+1, y);
        }
        if(y-1>=0 && board[x][y-1]=='O'){
            board[x][y-1]='N';
            DFS(board, x, y-1);
        }
        if(y+1<cols && board[x][y+1]=='O'){
            board[x][y+1]='N';
            DFS(board, x, y+1);
        }
    }
    void solve(vector<vector<char>> &board) {
        // Start typing your C/C++ solution below
        // DO NOT write int main() function
        if(board.empty() || board[0].empty())
            return;
        int rows = board.size();
        int cols = board[0].size();
        //scan the first and last rows and do DFS
        for(int i=0; i<cols; ++i){
            if(board[0][i]=='O'){
                board[0][i]='N';
                DFS(board, 0, i);
            }
            if(board[rows-1][i]=='O'){
                board[rows-1][i]='N';
                DFS(board, rows-1, i);
            }
        }
        //scan the first and last colomns and do DFS
        for(int i=1; i<rows-1; ++i){
            if(board[i][0]=='O'){
                board[i][0]='N';
                DFS(board, i, 0);
            }
            if(board[i][cols-1]=='O'){
                board[i][cols-1]='N';
                DFS(board, i, cols-1);
            }
        }
        //fill the board
        for(int i=0; i<rows; ++i)
            for(int j=0; j<cols; ++j){
                if(board[i][j]=='O')
                    board[i][j]='X';
                if(board[i][j]=='N')
                    board[i][j]='O';
            }
    }
}; 

Wednesday, October 23, 2013

Palindrome Partitioning II [Leetcode]

Given a string s, partition s such that every substring of the partition is a palindrome.
Return the minimum cuts needed for a palindrome partitioning of s.
For example, given s = "aab",
Return 1 since the palindrome partitioning ["aa","b"] could be produced using 1 cut.

Solution: of course we will get TLE using backtracking here. Basically, it is always good to give DP a shot to solve optimization problem [other good solutions or explanations: 1, 2, 3, 4]
class Solution {
public:
    int minCut(string s) {
        // Note: The Solution object is instantiated only once and is reused by each test case.
        if(s.empty()) return 0;
        
        int n = s.size();
        vector<vector<bool> > isP(n, vector<bool>(n, false));
        vector<int> minC(n, INT_MAX);
        
        //computer all palindromes
        for(int i=n-1; i>=0; i--)
            for(int j=i; j<n; j++){
                if(s[i]==s[j] && (j-i<2 || isP[i+1][j-1]))
                    isP[i][j]=true;
                else
                    isP[i][j]=false;
            }
        
        //DP
        //minC[i] = 0 if s[0,..., i] is palindrome
        //        = min(minC[j]+1) for all 0<=j<i && s[j+1,..., i] is palindrome
        for(int i=0; i<n; i++){
            int minI = INT_MAX;
            if(isP[0][i]){
                minC[i] = 0;
                continue;
            }
            for(int j=0; j<i; j++){
                if(isP[j+1][i])
                    minI = min(minI, minC[j]+1);
            }
            minC[i] = minI;
        }
        
        return minC[n-1];
    }
};