Showing posts with label BFS. Show all posts
Showing posts with label BFS. Show all posts

Wednesday, December 11, 2019

Leetcode 1278: Palindrome Partitioning III

https://leetcode.com/problems/palindrome-partitioning-iii/description/


Notes:

This question is a typical dfs question, and can be optimized by memorization.

We need to continuously cut the original string into K pieces. Let say the size of the string is N, and N >= K.

If we cut at the position of the first char, then the remaining string needs to be cut into (K-1) pieces.

Now it is clear that this question becomes the same question but with a smaller size. So we can solve it recursively, or by dfs.

And also, the key parameters are the length of the string and the number of pieces to be cut.

If we use the index of the string to label the position of the starting point, then the two key parameters can be labeled as i and K.

What we need to find is memo(0, K), representing the minimum operations needed.

Once we make one cut at position i, then the total operations for this way is Operation to make string from 0 to i becomes palindrome + memo(i+1, K-1).

It is relative straightforward to find how many operation needed to make a string in palindrome.

So memo(0, K) is the smallest value from all possible cuts.

Thus, this question can be solved by dfs + memorization. (So it can also be solved by dp, which is equivalent)

So the TC of this question is O(N/K * N * K) ~ O(N^2), and SC is O(N*K).

See the code below:

class Solution {
public:
    int palindromePartition(string s, int k) {
        int n = s.size();
        vector<vector<int>> memo(n, vector<int>(k+1, -1));
        return dfs(s, 0, k, memo);
    }

private:
    int dfs(string &s, int i, int ct, vector<vector<int>> &memo) {
        if(s.size() == ct || i==s.size()) return 0;
        if(ct == 1) return find(s, i, s.size()-1);
        if(memo[i][ct] != -1) return memo[i][ct];
        int t = s.size();
        for(int j=1; i+j-1+ct<=s.size(); ++j) {
            int one = find(s, i, i+j-1);
            t = min(t, one + dfs(s, i+j, ct-1, memo));
        }
        return memo[i][ct] = t;
    }
    int find(string& s, int i, int j) {
        int res = 0;
        while(i<j) {
            if(s[i++] != s[j--]) ++res;
        }
        return res;
    }
};


Sunday, December 8, 2019

Leetcode 1284: Minimum Number of Flips to Convert Binary Matrix to Zero Matrix

https://leetcode.com/problems/minimum-number-of-flips-to-convert-binary-matrix-to-zero-matrix/description/


Notes:

This question seems very complicated, which is labeled as hard. But actually it can be solved by the BFS. The size of the matrix is very small, so it is doable by the BFS.

The BFS method is known to find the length of the shortest path(s).

For this question, in order to better memorize the visited "conditions", we can convert the matrix into a string.

Some details for the indexes: if an element has a position of (x, y) in matrix which has m rows and n columns, then the corresponding index in the string is x*n + y. We can also recalculate the position of the element in the matrix if we only know the index in the string.

As required by the question, when we flip on element in the matrix, we need to flip all its four neighbors, which is a very typical operation in the BFS. Since we have converted the matrix into a string, we need to pay attention to the indexes using the above formula.

As mentioned above, a hash table will be used to memorize the visited "string", and the target is the string with all '0' chars of the correct length (m*n).

Then we can perform a layer-by-layer BFS. If the target can be found during search, return the number of the layers; otherwise, return -1.

See the code below:

class Solution {
public:
    int minFlips(vector<vector<int>>& mat) {
        int res = 0, m = mat.size(), n = mat[0].size();
        string s = "", end = "";
        for(auto &a : mat) {
            for(auto &b : a) {
                s.push_back(b+'0');
                end.push_back('0');
            }
        }
        if(s == end) return res;
        queue<string> q;
        unordered_map<string, int> mp;
        vector<int> dirs = {-1, 0, 1, 0, -1};
        q.push(s);
        mp[s] = 1;
        while(q.size()) {
            ++res;
            int k = q.size();
            for(int i=0; i<k; ++i) {
                auto t = q.front();
                q.pop();
                for(int j=0; j<t.size(); ++j) {
                    string st = t;
                    if(st[j] == '0') st[j] = '1';
                    else st[j] = '0';
                    int x = j/n, y = j%n;
                    for(int z = 0; z+1<dirs.size(); ++z) {
                        int nx = x + dirs[z], ny = y + dirs[z+1];
                        if(nx<0 || nx>=m || ny<0 || ny>=n) continue;
                        int id = nx*n + ny;
                        if(st[id] == '0') st[id] = '1';
                        else st[id] = '0';
                    }
                    if(st == end) return res;
                    if(!mp.count(st)) {
                        q.push(st);
                        mp[st] = 1;
                    }
                }
            }
        }
        return -1;
    }
};

Saturday, November 16, 2019

Leetcode 1257: Smallest Common Region

https://leetcode.com/contest/biweekly-contest-13/problems/smallest-common-region/


Notes:

This is a good question, which is equivalent to find the lowest common ancestor (LCA).

Since this is not a tree structure, we can build the relationship from children to parents, which then is much easier to find the common ancestor.

One way is that we can use BFS: start with the two strings, then gradually search the upper layers layer by layer. The first found common string is the LCA.

One corner case is that: one of the string may reach the root first. In this case, we need to make sure, the search can continue until find the LCA.

See the code below:

class Solution {
public:
    string findSmallestRegion(vector<vector<string>>& regions, string region1, string region2) {
        unordered_map<string, string> ps;//parents
        unordered_map<string, bool> visit;
        for(auto &a : regions) {
            for(int i=1; i<a.size(); ++i) ps[a[i]] = a[0];
        }
        queue<string> q;
        q.push(region1);
        visit[region1] = true;
        q.push(region2);
        visit[region2] = true;
        while(q.size()) {
            int k = q.size();
            for(int i=0; i<k; ++i) {
                auto t = q.front();
                q.pop();
                if(ps[t] != "") {//corner case: when one of them reach the root
                    string a = ps[t];
                    if(visit.count(a)) return a;
                    q.push(a);
                    visit[a] = true;
                }
            }
        }
        return "";//if there is no LCA
    }
};

Sunday, November 10, 2019

Leetcode 1254. Number of Closed Islands

https://leetcode.com/problems/number-of-closed-islands/description/


Notes:

The question can be viewed as a follow-up of the Number of Islands. For this one, we need to check the neighbors of the most out layer of the island. If one of them is boundary, it is not a closed island.

So we can do this in a BFS naturally, since BFS expands layer-by-layer gradually.

See the code below:

class Solution {
public:
    int closedIsland(vector<vector<int>>& grid) {
        int res = 0;
        int m = grid.size(), n = grid[0].size();
        vector<vector<int>> visit(m, vector<int>(n, 0));//not needed if we can modify grid' values
        for(int i=0; i<m; ++i) {
            for(int j=0; j<n; ++j) {
                if(grid[i][j] == 0 && !visit[i][j]) {
                    bfs(grid, i, j, visit, res);
                }
            }
        }
        return res;
    }

private:
    void bfs(vector<vector<int>>& gd, int x, int y, vector<vector<int>>& visit, int &res) {
        queue<pair<int, int>> q;
        q.push({x, y});
        visit[x][y] = 1;
        vector<int> dirs = {-1, 0, 1, 0, -1};
        bool ed = false;//this is the only difference
        while(q.size()) {
            auto t = q.front();
            q.pop();
            for(int i=0; i+1<dirs.size(); ++i) {
                int a = t.first + dirs[i], b = t.second + dirs[i+1];
                if(a<0 || a>= gd.size() || b<0 || b>=gd[0].size()) {
                    ed = true;//is edge
                    continue;
                }
                if(visit[a][b] || gd[a][b]) continue;
                q.push({a, b});
                visit[a][b] = 1;
            }
        }
        if(!ed) ++res;
    }
};

Saturday, November 2, 2019

Leetcode 1245: Tree Diameter

https://leetcode.com/contest/biweekly-contest-12/problems/tree-diameter/



Notes:

This is a very good question. So apparently, both DFS and BFS can be applied to solve this problem.

The naive DFS idea is we can start with any node, which is as the root of the tree, then we need to find the top 2 longest path down to the leaves. The sum of the two longest path should be the longest path. One detail is that we need to remember the prev-node, to reduce redundant traversal. The time complexity is O(N*N).

One BFS idea is very clear: start with any node A, then find the furthest node from A, call it B; then start with Node B, find the furthest Node C. Then the distance between B and C is the distance. So the time complexity is O(2*N) due to two traversals.

However, here I want to share another BFS method: from the edges to the roots, which only requires one traversal.

We need to count the indgrees of each node. Then we start with all the nodes with indegree of 1. Then layer-by-layer approaches the roots.

There may be one final node as the roots, or two nodes. If only node left, we just need to double number of layers in the BFS; otherwise, we need to add 1.

Apparently, we assume there is no loop existing in this question.

See the code below:

class Solution {
public:
    int treeDiameter(vector<vector<int>>& edges) {
        unordered_map<int, vector<int>> gh;
        unordered_map<int, int> ind;
        for(auto &a : edges) {
            gh[a[0]].push_back(a[1]);
            gh[a[1]].push_back(a[0]);
            ++ind[a[0]];
            ++ind[a[1]];
        }
        queue<int> q;
        for(auto &a : ind) {
            if(a.second == 1) {
                q.push(a.first);
                --ind[a.first];
            }
        }
        int step = 0, last = 0;
        while(q.size()) {
            int k = q.size();
            for(int i=0; i<k; ++i) {
                auto t = q.front();
                q.pop();
                for(auto &a : gh[t]) {
                    --ind[a];
                    if(ind[a] == 1) q.push(a);
                }
            }
            ++step;
            if(q.empty() && k == 2) last = 1;
        }
        return (step-1)*2 + last;
    }
};


Wednesday, October 23, 2019

Lintcode 803: Shortest Distance from All Buildings

https://www.lintcode.com/problem/shortest-distance-from-all-buildings/description

You want to build a house on an empty land which reaches all buildings in the shortest amount of distance. You can only move up, down, left and right. You are given a 2D grid of values 0, 1 or 2, where:
  • Each 0 marks an empty land which you can pass by freely.
  • Each 1 marks a building which you cannot pass through.
  • Each 2 marks an obstacle which you cannot pass through.

Example

Example 1
Input: [[1,0,2,0,1],[0,0,0,0,0],[0,0,1,0,0]]
Output: 7
Explanation:
In this example, there are three buildings at (0,0), (0,4), (2,2), and an obstacle at (0,2).
1 - 0 - 2 - 0 - 1
|   |   |   |   |
0 - 0 - 0 - 0 - 0
|   |   |   |   |
0 - 0 - 1 - 0 - 0
The point (1,2) is an ideal empty land to build a house, as the total travel distance of 3+3+1=7 is minimal. So return 7.
Example 2
Input: [[1,0],[0,0]]
Output: 1
In this example, there is one buildings at (0,0).
1 - 0
|   |
0 - 0
The point (1,0) or (0,1) is an ideal empty land to build a house, as the total travel distance of 1 is minimal. So return 1.

Notes:

This question is labeled as hard in both Leetcode and Lintcode, but actually the idea is pretty straightforward:

1): we can use bfs search for each house, then record the minimum distance results for each "empty" position into a hash map;

2): then we need to go through the hash map, to find the empty position having the minimum sum distances to all the houses.

See the code below:

class Solution {
public:
    /**
     * @param grid: the 2D grid
     * @return: the shortest distance
     */
    int shortestDistance(vector<vector<int>> &grid) {
        // write your code here
        if(!grid.size() || !grid.front().size()) return 0;
        int res = INT_MAX, m = grid.size(), n = grid.front().size(), ct = 0;
        unordered_map<int, vector<int>> mp;
        for(int i=0; i<m; ++i) {
            for(int j=0; j<n; ++j) {
                if(grid[i][j] == 1) {
                    ++ct;
                    bfs(grid, i, j, mp);
                }
            }
        }
        for(int i=0; i<m; ++i) {
            for(int j=0; j<n; ++j) {
                if(grid[i][j] == 0 && mp[i*n+j].size() == ct) {
                    int sum = 0;
                    for(auto &a : mp[i*n+j]) sum += a;
                    res = min(res, sum);
                }
            }
        }
        return res==INT_MAX? -1 : res;
    }

private:
    void bfs(vector<vector<int>>& grid, int i, int j, unordered_map<int, vector<int>>& mp) {
        int m = grid.size(), n = grid.front().size();
        vector<vector<int>> visit(m, vector<int>(n, 0));
        queue<pair<int, int>> q;
        q.push({i, j});
        visit[i][j] = 1;
        vector<int> dirs = {-1, 0, 1, 0, -1};
        int step = 1;
        while(q.size()) {
            int k = q.size();
            for(int i=0; i<k; ++i) {
                auto t = q.front();
                q.pop();
                for(int j=0; j+1<dirs.size(); ++j) {
                    int a = t.first + dirs[j], b = t.second + dirs[j+1];
                    if(a<0 || a>=m || b<0 || b>=n || grid[a][b] != 0 || visit[a][b]) continue;
                    q.push({a, b});
                    visit[a][b] = 1;
           //         cout<<a<<" "<<b<<" "<<step<<endl;
                    mp[a*n+b].push_back(step);
                }
            }
            ++step;
        }
    }
};

Wednesday, October 16, 2019

Leetcode 778: Swim in Rising Water

https://leetcode.com/problems/swim-in-rising-water/description/

On an N x N grid, each square grid[i][j] represents the elevation at that point (i,j).
Now rain starts to fall. At time t, the depth of the water everywhere is t. You can swim from a square to another 4-directionally adjacent square if and only if the elevation of both squares individually are at most t. You can swim infinite distance in zero time. Of course, you must stay within the boundaries of the grid during your swim.
You start at the top left square (0, 0). What is the least time until you can reach the bottom right square (N-1, N-1)?
Example 1:
Input: [[0,2],[1,3]]
Output: 3
Explanation:
At time 0, you are in grid location (0, 0).
You cannot go anywhere else because 4-directionally adjacent neighbors have a higher elevation than t = 0.

You cannot reach point (1, 1) until time 3.
When the depth of water is 3, we can swim anywhere inside the grid.
Example 2:
Input: [[0,1,2,3,4],[24,23,22,21,5],[12,13,14,15,16],[11,17,18,19,20],[10,9,8,7,6]]
Output: 16
Explanation:
 0  1  2  3  4
24 23 22 21  5
12 13 14 15 16
11 17 18 19 20
10  9  8  7  6

The final route is marked in bold.
We need to wait until time 16 so that (0, 0) and (4, 4) are connected.
Note:
  1. 2 <= N <= 50.
  2. grid[i][j] is a permutation of [0, ..., N*N - 1].


Notes:

This question basically asks for the minimum of the maximum of any path from the source to the end.

So binary search + bfs (or dsf) can solve this problem.

The basic idea is that: the minimum must be inside a range. When it is above some value, we can always visit the end from the source; otherwise, we cannot. So binary search will work.

For each proposed value, we just run a bfs, to see whether we can visit the end or not. If not, we need to increase the left boundary; otherwise, decrease the right boundary.

See the code below:

class Solution {
public:
    int swimInWater(vector<vector<int>>& grid) {
        int m = grid.size(), n = grid.front().size(), left = 0, right = m*n;
        while(left < right) {
            int mid = left + (right - left) / 2;
            if(!canFinish(mid, grid)) left = mid + 1;
            else right = mid;
        }
        return left;
    }

private:
    bool canFinish(int mid, vector<vector<int>>& grid) {
        if(grid[0][0] > mid) return false;
        int m = grid.size(), n = grid.front().size();
        if(grid[m-1][n-1] > mid) return false;
        vector<vector<int>> visit(m, vector<int>(n, 0));
        queue<pair<int, int>> q;
        q.push({0, 0});
        visit[0][0] = 1;
        vector<int> dirs = {-1, 0, 1, 0, -1};
        while(q.size()) {
            auto t = q.front();
            q.pop();
            for(int i=0; i+1 < dirs.size(); ++i) {
                int x = t.first + dirs[i], y = t.second + dirs[i+1];
                if(x<0 || x>=m || y<0 || y>=n || visit[x][y] || grid[x][y] > mid) continue;
                if(x == m-1 && y == n-1) return true;
                visit[x][y] = 1;
                q.push({x, y});
            }
        }
        return false;
    }
};

Saturday, September 28, 2019

Leetcode 1210: Minimum Moves to Reach Target with Rotations

https://leetcode.com/problems/minimum-moves-to-reach-target-with-rotations/description/



Notes:

This question looks like can be solved by dp, but actually the state transition equation is not easy to be converted into code. (since we cannot simply run the loop from smaller index to larger, or from larger to smaller, due to the possible rotations.)

This question is asking for the minimum distance, so it can be naturally solved by BFS.

There are two positions: one is horizontal and the other is vertical. So we need to distinguish them.

Another thing is that the final valid state must be horizontal.


See the code below:

class Solution {
public:
    int minimumMoves(vector<vector<int>>& grid) {
        int res = 0, n = grid.size();
        vector<vector<int>> visit(n*n, vector<int>(2, 0));
        queue<pair<int, int>> q;
        q.push({0*n+1, 0});
        visit[1][0] = 1;
        while(q.size()) {
            ++res;
            int k = q.size();
            for(int i=0; i<k; ++i) {
                auto t = q.front();
                q.pop();
                int x = t.first/n, y = t.first%n, z = t.second;
                if(z==0) {//if horizontal
                    if(y+1<n && grid[x][y+1] == 0 && !visit[x*n+y+1][0]) {
                        if(x == n-1 && y + 1 == n-1) return res;
                        q.push({x*n+y+1, 0});
                        visit[x*n+y+1][0] = 1;
                    }
                    if(x+1<n && y-1>=0 && grid[x+1][y-1]==0 && grid[x+1][y]==0) {
                        if(!visit[(x+1)*n+y-1][1]) {
                            q.push({(x+1)*n+y-1, 1});
                            visit[(x+1)*n+y-1][1] = 1;
                        }
                        if(!visit[(x+1)*n+y][0]) {
                            if(x+1 == n-1 && y == n-1) return res;
                            q.push({(x+1)*n + y, 0});
                            visit[(x+1)*n+y][0] = 1;
                        }
                    }
                }
                if(z==1) {//if vertical
                    if(x+1<n && grid[x+1][y] == 0 && !visit[(x+1)*n+y][1]) {
                        q.push({(x+1)*n+y, 1});
                        visit[(x+1)*n+y][1] = 1;
                    }
                    if(x-1>= 0 && y+1<n && grid[x-1][y+1]==0 && grid[x][y+1]==0) {
                        if(!visit[(x-1)*n+y+1][0]) {
                            q.push({(x-1)*n+y+1, 0});
                            visit[(x-1)*n+y+1][0] = 1;
                        }
                        if(!visit[(x)*n+y+1][1]) {
                            q.push({(x)*n + y+ 1, 1});
                            visit[(x)*n+y+1][1] = 1;
                        }
                    }
                }
            }
        }
        return -1;
    }
};