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

LeetCode 847 - Shortest Path Visiting All Nodes


https://leetcode.com/problems/shortest-path-visiting-all-nodes/
An undirected, connected graph of N nodes (labeled 0, 1, 2, ..., N-1) is given as graph.
graph.length = N, and j != i is in the list graph[i] exactly once, if and only if nodes i and j are connected.
Return the length of the shortest path that visits every node. You may start and stop at any node, you may revisit nodes multiple times, and you may reuse edges.

    Example 1:
    Input: [[1,2,3],[0],[0],[0]]
    Output: 4
    Explanation: One possible path is [1,0,2,0,3]
    Example 2:
    Input: [[1],[0,2,4],[1,3,4],[2],[1,2]]
    Output: 4
    Explanation: One possible path is [0,1,4,2,3]
    

    Note:
    1. 1 <= graph.length <= 12
    2. 0 <= graph[i].length < graph.length

    https://blog.csdn.net/fuxuemingzhu/article/details/82939203
    话说看到这个题的第一感觉就是BFS,因为我们要找到遍历所有节点的最少步数,这个正是BFS擅长的。唯一不同的就是这个题允许从多个顶点出发,也就是说没有了固定的起点。那么需要对BFS稍微改变一点,即在初始化的时候,把所有顶点都放进队列之中,这样,每次把队列的元素pop出来一遍之后就是新的一轮循环,也就可以认为所有的节点都是同时向前迈进了一步。

    这个题使用了一个的技巧,位运算。一般的BFS过程都是只保存访问过的节点即可,因为每个节点只可以使用一次,但是这个题的节点可以访问多次,那么就是说必须维护一个实时的访问了哪些节点的状态。按道理说,如果不使用位运算而是使用字典等方式保存访问过了的状态也可以,但是,看了给出的图的顶点个数只有12个,哪怕一个int都会有32个bit够用,所以可以直接使用和图中顶点数相等的位数来保存这个状态是否访问过。这个状态怎么理解?从每个顶点出发到达,所有访问过的节点是状态。也就是说这个状态是全局唯一的,每个顶点都有2 * N个状态表示它访问的其他节点。有2 ^ N个bit,每个位都代表对应的节点是否访问过。最终的状态是(1 << N) - 1,即全是1,表示所有节点都访问了。

    这个visited是个二维数组,保存的是每个节点的所有状态,对于该题目的BFS,有可能有N * 2^N个状态,使用visited保存每个节点已经访问的状态,对应状态位置是0/1。

    时间复杂度是O(N * (2^N)),空间复杂度是O(N * 2^N)。

    https://tinacristal.github.io/2018/12/07/shor/
    用二进制的数来表示当前访问节点的状态
    4个节点 如1101表示 0,2,3号节点已访问过
    最后的终止状态是1111
    或者用Hashtable 也可以
    为了遍历所有的节点 一个节点可以被重复访问 所以不能用 vis[cur] 跳过访问过的节点
    但是不跳过访问过的状态 可能会陷入 0->1->0的死循环
    所以建立二维数组vis 记录当前访问过的节点和状态
    不会重复在这个节点的访问状态
    即 在1号节点已经访问过0号,1号房间
    pair<int,int> {1,0011}
    即在搜索1号节点的邻居时不会再访问0号节点 因为此时状态不会更新
    还是 0011
    https://github.com/cherryljr/LeetCode/blob/master/Shortest%20Path%20Visiting%20All%20Nodes.java
     * 题目要求走遍所有点的最短路径(最少步数)。而该图是一个 权值为1的无向图。
     * 并且数据规模为 node <= 12, 所以首先可以考虑使用 BFS 来做。
     *
     * 该题与平时遇到的 BFS 不同点在于,在同一条路径(同一轮遍历)中,一个点是可以被重复遍历的。
     * 而我们平时都是会记录一个 visited 数组来避免遍历重复的点,或者是限制加入队列的条件
     * 来减小问题的规模。否则就会出现死循环。而这也是本题的难点所在。
     *
     * 对此我们仍然可以通过记录 visited 状态来解决。
     * 只不过这里需要记录的状态为:当前的位置 以及 对应的遍历过的节点状态
     * 当我们从一个节点出发,遍历后没有新的节点增加的话,那就说明我们走的路是无用的。
     * 同时因为节点个数最多只有 12 个,所以我们可以通过 二进制 来表示状态从而达到优化的效果。
     *
     * 注:这里使用了 进队列 时进行判断的做法,代码上看上去可能没那么好看(for循环遍历邻居时)
     * 但是在时间上可以优化不少。
     * 关于这点的分析可以详细参考:
     *  https://github.com/cherryljr/LintCode/blob/master/Matrix%20Water%20Injection.java
    

        public int shortestPathLength(int[][] graph) {
            int n = graph.length;
            // 采用 入队列时判断 的BFS做法,因此需要处理一下起始情况。
            if (n <= 1) {
                return 0;
            }
    
            Queue<int[]> queue = new LinkedList<>();
            boolean[][] visited = new boolean[n][1 << n];
            // 可以选择任意点作为起点,因此开始时需要将所有的节点都 add 到队列中
            for (int i = 0; i < n; i++) {
                queue.offer(new int[]{i, 1 << i});
                visited[i][1 << i] = true;
            }
            // 采用二进制记录状态信息
            int target = (1 << n) - 1;
    
            int step = 0;
            while (!queue.isEmpty()) {
                int size = queue.size();
                for (int i = 0; i < size; i++) {
                    int[] curr = queue.poll();
                    int pos = curr[0], state = curr[1];
    
                    for (int neigh : graph[pos]) {
                        int nextState = state | (1 << neigh);
                        if (nextState == target) {
                            return step + 1;
                        }
                        if (visited[neigh][nextState]) {
                            continue;
                        }
                        visited[neigh][nextState] = true;
                        queue.offer(new int[]{neigh, state | (1 << neigh)});
                    }
                }
                step++;
            }
    
            return -1;
        }
    https://leetcode.com/problems/shortest-path-visiting-all-nodes/discuss/135686/Java-DP-Solution
    https://leetcode.com/problems/shortest-path-visiting-all-nodes/discuss/135809/Fast-BFS-Solution-(46ms)-Clear-Detailed-Explanation-Included
    we could get rid of the cost attribute just by looping the queue by levels. It will cause less confusion. See
    https://leetcode.com/articles/shortest-path-visiting-all-nodes/
    Approach #1: Breadth First Search [Accepted]
    A path 'state' can be represented as the subset of nodes visited, plus the current 'head' node. Then, the problem reduces to a shortest path problem among these states, which can be solved with a breadth-first search.
    Let's call the set of nodes visited by a path so far the cover, and the current node as the head. We'll store the cover states using set bits: k is in the cover if the kth bit of cover is 1.
    For states state = (cover, head), we can perform a breadth-first search on these states. The neighbors are (cover | (1 << child), child) for each child in graph[head].
    If at any point we find a state with all set bits in it's cover, because it is a breadth-first search, we know this must represent the shortest path length.
    • Time Complexity: O(2^N * N).
    • Space Complexity: O(2^N * N).
      public int shortestPathLength(int[][] graph) {
        int N = graph.length;
        Queue<State> queue = new LinkedList<>();
        int[][] dist = new int[1 << N][N];
        for (int[] row : dist)
          Arrays.fill(row, N * N);

        for (int x = 0; x < N; ++x) {
          queue.offer(new State(1 << x, x));
          dist[1 << x][x] = 0;
        }

        while (!queue.isEmpty()) {
          State node = queue.poll();
          int d = dist[node.cover][node.head];
          if (node.cover == (1 << N) - 1)
            return d;

          for (int child : graph[node.head]) {
            int cover2 = node.cover | (1 << child);
            if (d + 1 < dist[cover2][child]) {
              dist[cover2][child] = d + 1;
              queue.offer(new State(cover2, child));

            }
          }
        }

        throw null;
      }

    class State {
      int cover, head;

      State(int c, int h) {
        cover = c;
        head = h;
      }

    }


    Approach #2: Dynamic Programming [Accepted]
    http://bookshadow.com/weblog/2018/06/03/leetcode-shortest-path-visiting-all-nodes/
    Floyd + 动态规划(Dynamic Programming)
    时间复杂度 O(2^n * n^2)
    利用Floyd求出每对顶点i, j之间的最短距离,记为dp[i][j],代价为O(N^3)
    
    利用status[s][i]记录:状态为s,当前所在节点为i时的最小路径长度
    
    状态s是二进制,表示各节点是否被访问过,1表示已访问,0表示未访问
    
    状态转移方程:
    
    status[ns][j] = min(status[ns][j], status[s][i] + dp[i][j])
    
    其中ns表示从状态s的i点出发到达j点时的新状态


    A path 'state' can be represented as the subset of nodes visited, plus the current 'head' node. As in Approach #1, we have a recurrence in states: answer(cover, head) is min(1 + answer(cover | (1<<child), child) for child in graph[head]). Because these states form a DAG (a directed graph with no cycles), we can do dynamic programming.
    Algorithm
    Let's call the set of nodes visited by a path so far the cover, and the current node as the head. We'll store dist[cover][head] as the length of the shortest path with that cover and head. We'll store the coverstates using set bits, and maintain the loop invariant (on cover), that dist[k][...] is correct for k < cover.
    For every state (cover, head), the possible next (neighbor) nodes in the path are found in graph[head]. The new cover2 is the old cover plus next.
    For each of these, we perform a "relaxation step" (for those familiar with the Bellman-Ford algorithm), where if the new candidate distance for dist[cover2][next] is larger than dist[cover][head] + 1, we'll update it to dist[cover][head] + 1.
    Care must be taken to perform the relaxation step multiple times on the same cover if cover = cover2. This is because a minimum state for dist[cover][head] might only be achieved through multiple steps through some path.
    Finally, it should be noted that we are using implicitly the fact that when iterating cover = 0 .. (1<<N) - 1, that each new cover cover2 = cover | 1 << x is such that cover2 >= cover. This implies a topological ordering, which means that the recurrence on these states form a DAG.
    • Time Complexity: O(2^N * N).
    • Space Complexity: O(2^N * N).
      public int shortestPathLength(int[][] graph) {
        int N = graph.length;
        int dist[][] = new int[1 << N][N];
        for (int[] row : dist)
          Arrays.fill(row, N * N);
        for (int x = 0; x < N; ++x)
          dist[1 << x][x] = 0;

        for (int cover = 0; cover < 1 << N; ++cover) {
          boolean repeat = true;
          while (repeat) {
            repeat = false;
            for (int head = 0; head < N; ++head) {
              int d = dist[cover][head];
              for (int next : graph[head]) {
                int cover2 = cover | (1 << next);
                if (d + 1 < dist[cover2][next]) {
                  dist[cover2][next] = d + 1;
                  if (cover == cover2)
                    repeat = true;
                }
              }
            }
          }
        }

        int ans = N * N;
        for (int cand : dist[(1 << N) - 1])
          ans = Math.min(cand, ans);
        return ans;

      }



    LeetCode 97 - Interleaving String


    http://buttercola.blogspot.com/2014/09/leetcode-interleaving-string.html
    Given s1s2s3, 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.
    X. DP
    这道求交织相错的字符串和之前那道 Word Break 拆分词句 的题很类似,就想我之前说的只要是遇到字符串的子序列或是匹配问题直接就上动态规划Dynamic Programming,其他的都不要考虑,什么递归呀的都是浮云,千辛万苦的写了递归结果拿到OJ上妥妥Time Limit Exceeded,能把人气昏了,所以还是直接就考虑DP解法省事些。一般来说字符串匹配问题都是更新一个二维dp数组,核心就在于找出递推公式。那么我们还是从题目中给的例子出发吧,手动写出二维数组dp如下:

    复制代码
      Ø d b b c a
    Ø T F F F F F
    a T F F F F F
    a T T T T T F
    b F T T F T F
    c F F T T T T
    c F F F T F T
    复制代码

    首先,这道题的大前提是字符串s1和s2的长度和必须等于s3的长度,如果不等于,肯定返回false。那么当s1和s2是空串的时候,s3必然是空串,则返回true。所以直接给dp[0][0]赋值true,然后若s1和s2其中的一个为空串的话,那么另一个肯定和s3的长度相等,则按位比较,若相同且上一个位置为True,赋True,其余情况都赋False,这样的二维数组dp的边缘就初始化好了。下面只需要找出递推公式来更新整个数组即可,我们发现,在任意非边缘位置dp[i][j]时,它的左边或上边有可能为True或是False,两边都可以更新过来,只要有一条路通着,那么这个点就可以为True。那么我们得分别来看,如果左边的为True,那么我们去除当前对应的s2中的字符串s2[j - 1] 和 s3中对应的位置的字符相比(计算对应位置时还要考虑已匹配的s1中的字符),为s3[j - 1 + i], 如果相等,则赋True,反之赋False。 而上边为True的情况也类似,所以可以求出递推公式为:
    dp[i][j] = (dp[i - 1][j] && s1[i - 1] == s3[i - 1 + j]) || (dp[i][j - 1] && s2[j - 1] == s3[j - 1 + i]);
    其中dp[i][j] 表示的是 s2 的前 i 个字符和 s1 的前 j 个字符是否匹配 s3 的前 i+j 个字符,根据以上分析.

    https://robinliu.gitbooks.io/algorithms/content/shuang_xu_lie_xing.html
    双序列型
    状态: f[i][j] 表示第一个 sequence 的前 i 个, 与第二个 sequence 的前 j 个的关系
    方程: f[i][j] 第 i 个和第 j 个的匹配关系
    初始化: f[i][0] 和 f[0][j]
    答案: f[m][n]

    state: f[i][j]: 表示 s1 的前 i 个字符和 s2 的前 j 个字符能否组成 s3 的前 i+j 个字符
    fucntion: f[i][j] = (f[i-1][j] && s1[i-1] == s3[i+j-1]) || (f[i][j-1] && s2[j-1] == s3[i+j-1])

    https://discuss.leetcode.com/topic/7728/dp-solution-in-java
    We can use DP to solve this problem. So the steps are:

    • Define boolean dp[str1.length() + 1][str2.length() + 1], where dp[ i ][ j ] : means whether str1.charAt(0) to str1.charAt(i) and str2.charAt(0) to str2.charAt(j) could construct the string str3, from 0 to i + j. 
    • Transit function: 
      • dp[i][j]  = str3.charAt(i + j) == str1.charAt(i) && dp[ i - 1][j] || 
      • str3.charAt(i + j) == str2.charAt(j) && dp[i][j - 1]
    • Initial State 
      • dp[0][0] = true;
      • dp[0][j] = str3.charAt(j) == str2.charAt(j)
      • dp[i][0] = str3.charAt(i) == str1.charAt(i)
    • Final state dp[str1.length()][str2.length()]. 
        public boolean isInterleave(String s1, String s2, String s3) {
            if (s1 == null || s2 == null || s3 == null) {
                return false;
            }
             
            if (s1.length() == 0 && s2.length() == 0 && s3.length() == 0) {
                return true;
            }
             
            if (s1.length() + s2.length() != s3.length()) {
                return false;
            }
             
            boolean[][] dp = new boolean[s1.length() + 1][s2.length() + 1];
            dp[0][0] = true;
             
            for (int i = 1; i <= s2.length(); i++) {
                if (s2.charAt(i - 1) == s3.charAt(i - 1)) {
                    dp[0][i] = true;
                } else break;
            }
             
            for (int i = 1; i <= s1.length(); i++) {
                if (s1.charAt(i - 1) == s3.charAt(i - 1)) {
                    dp[i][0] = true;
                } else break;
            }
             
            for (int i = 1; i <= s1.length(); i++) {
                for (int j = 1; j <= s2.length(); j++) {
                    if (s3.charAt(i + j - 1) == s1.charAt(i - 1)) {
                        dp[i][j] = dp[i - 1][j];
                    }
                     
                    if (s3.charAt(i + j - 1) == s2.charAt(j - 1)) {
                        dp[i][j] = dp[i][j] || dp[i][j - 1];
                    }
                }
            }
             
            return dp[s1.length()][s2.length()];
        }
    我们也可以把for循环合并到一起,用if条件来处理边界情况,整体思路和上面的解法没有太大的区别
      public boolean isInterleave(String s1, String s2, String s3) {
        if (s3.length() != s1.length() + s2.length()) {
          return false;
        }
        boolean dp[][] = new boolean[s1.length() + 1][s2.length() + 1];
        for (int i = 0; i <= s1.length(); i++) {
          for (int j = 0; j <= s2.length(); j++) {
            if (i == 0 && j == 0) {
              dp[i][j] = true;
            } else if (i == 0) {
              dp[i][j] = dp[i][j - 1] && s2.charAt(j - 1) == s3.charAt(i + j - 1);
            } else if (j == 0) {
              dp[i][j] = dp[i - 1][j] && s1.charAt(i - 1) == s3.charAt(i + j - 1);
            } else {
              dp[i][j] = (dp[i - 1][j] && s1.charAt(i - 1) == s3.charAt(i + j - 1))
                  || (dp[i][j - 1] && s2.charAt(j - 1) == s3.charAt(i + j - 1));
            }
          }
        }
        return dp[s1.length()][s2.length()];

      }
    http://www.jiuzhang.com/solutions/interleaving-string/
        public boolean isInterleave(String s1, String s2, String s3) {
            if (s1.length() + s2.length() != s3.length()) {
                return false;
            }
            
            boolean [][] interleaved = new boolean[s1.length() + 1][s2.length() + 1];
            interleaved[0][0] = true;
            
            for (int i = 1; i <= s1.length(); i++) {
                if(s3.charAt(i - 1) == s1.charAt(i - 1) && interleaved[i - 1][0])
                    interleaved[i][0] = true;
            }
            
            for (int j = 1; j <= s2.length(); j++) {
                if(s3.charAt(j - 1) == s2.charAt(j - 1) && interleaved[0][j - 1])
                    interleaved[0][j] = true;
            }
            
            for (int i = 1; i <= s1.length(); i++) {
                for (int j = 1; j <= s2.length(); j++) {
                    if(((s3.charAt(i + j - 1) == s1.charAt(i - 1) && interleaved[i - 1][j]))
                        || ((s3.charAt(i + j - 1)) == s2.charAt(j - 1) && interleaved[i][j - 1]))
                    interleaved[i][j] = true;
                }
            }
            
            return interleaved[s1.length()][s2.length()];
        }
    

    X.DP - Space Optimization
    https://discuss.leetcode.com/topic/5087/my-solution-in-java-using-dp-time-o-n-m-and-space-o-m
      public boolean isInterleave(String s1, String s2, String s3) {
        if (s3.length() != s1.length() + s2.length()) {
          return false;
        }
        boolean dp[] = new boolean[s2.length() + 1];
        for (int i = 0; i <= s1.length(); i++) {
          for (int j = 0; j <= s2.length(); j++) {
            if (i == 0 && j == 0) {
              dp[j] = true;
            } else if (i == 0) {
              dp[j] = dp[j - 1] && s2.charAt(j - 1) == s3.charAt(i + j - 1);
            } else if (j == 0) {
              dp[j] = dp[j] && s1.charAt(i - 1) == s3.charAt(i + j - 1);
            } else {
              dp[j] = (dp[j] && s1.charAt(i - 1) == s3.charAt(i + j - 1))
                  || (dp[j - 1] && s2.charAt(j - 1) == s3.charAt(i + j - 1));
            }
          }
        }
        return dp[s2.length()];

      }
        public boolean isInterleave(String s1, String s2, String s3) {
            if (s1==null || s2==null || s3==null) return false;
            if (s1.length()==0 && s2.length()==0 && s3.length()==0) return true;
            if ((s1.length()+s2.length())!=s3.length()) return false;
            boolean valid[] = new boolean[s2.length()+1];
            valid[0] = false;
            for (int j=1; j<=s2.length(); j++){
                if (s2.charAt(j-1)==s3.charAt(j-1)) valid[j]=true;
            }
            for (int i=1; i<=s1.length(); i++){
                if (s1.charAt(i-1)==s3.charAt(i-1)) valid[0]=true;
                for (int j=1; j<=s2.length(); j++){
                    valid[j] = (valid[j] && s1.charAt(i-1)==s3.charAt(i+j-1)) ||
                               (valid[j-1] && s2.charAt(j-1)==s3.charAt(i+j-1));
                }
            }
            return valid[s2.length()];
        }

    public static boolean isInterleaveOptz(String s1, String s2, String s3) {
        if (s3.length() != s1.length() + s2.length()) return false;
    
        boolean[] optimal = new boolean[s2.length() + 1];    //dp optimal
        optimal[0] = true;
        for (int j = 0; j < s2.length(); j++) { //check no s1 char is selected, if s2 could equals to s3
            if (optimal[j] && s2.charAt(j) == s3.charAt(j)) optimal[j + 1] = true;
        }
    
        for (int i = 0; i < s1.length(); i++) { //check select i-th char in s1
            if (optimal[0] && s1.charAt(i) == s3.charAt(i)) optimal[0] = true;    //no char in s2 is selected
            else optimal[0] = false;
            for (int j = 0; j < s2.length(); j++) {  //select j-th char
                if ((s1.charAt(i) == s3.charAt(i + j + 1) && optimal[j + 1]) ||
                        s2.charAt(j) == s3.charAt(i + j + 1) && optimal[j]) {
                    optimal[j + 1] = true;
                } else optimal[j + 1] = false;
            }
        }
        return optimal[s2.length()];
    }
    时间O(m*n) 空间O(min(m,n))
    public boolean isInterleave(String s1, String s2, String s3) {
        if (s1.length() + s2.length() != s3.length()) {
            return false;
        }
        String maxWord = s1.length() > s2.length() ? s1 : s2;
        String minWord = s1.length() > s2.length() ? s2 : s1;
        boolean[]dp = new boolean[minWord.length() + 1];
        dp[0] = true;
       
        for (int i = 0; i < minWord.length(); i++) {
            if (minWord.charAt(i) == s3.charAt(i) && dp[i] == true) {
                dp[i + 1] = true;
            }
        }
        for (int i = 1; i <= maxWord.length(); i++) {
            dp[0] = dp[0] && maxWord.charAt(i - 1) == s3.charAt(i - 1); 
            for (int j = 1; j <= minWord.length(); j++) {
                dp[j] = s3.charAt(i + j - 1) == maxWord.charAt(i - 1) && dp[j] == true || s3.charAt(i + j - 1) == minWord.charAt(j - 1) && dp[j - 1] == true;
                //此处不能再用if判断, 因为dp[j]已经有值 必须要赋值true/false
            }
        }
        
        return dp[minWord.length()];
    }

        public boolean isInterleave(String s1, String s2, String s3) {
            // write your code here
         if (s1 == null || s2 == null || s3 == null) return false;
            if (s1.length() == 0 && s2.length() == 0 && s3.length() == 0) return true;
            if ((s1.length() + s2.length()) != s3.length()) return false;
         String shorterString, longerString;
         if (s1.length() < s2.length()) {
         shorterString = s1;
         longerString = s2;
         } else {
         shorterString = s2;
         longerString = s1;
         }
         boolean[] valid = new boolean[shorterString.length() + 1];
         valid[0] = true;
         for (int i = 1; i <= shorterString.length(); i++) {
             if (shorterString.charAt(i-1)==s3.charAt(i-1)) valid[i]=true;
         }
         for (int i = 1; i <= longerString.length(); i++) {
         for (int j = 1; j <= shorterString.length(); j++) {
         valid[j] = (valid[j] && longerString.charAt(i-1)==s3.charAt(i+j-1)) ||
                               (valid[j-1] && shorterString.charAt(j-1)==s3.charAt(i+j-1));
         }
         }
            return valid[shorterString.length()];
        }

    X. Recursive version
    1. public boolean isInterleave(String s1, String s2, String s3) {  
    2.       if(s1.length() == 0){  
    3.         if( s2.equals(s3))return true;  
    4.         else return false;  
    5.       }else if(s2.length() == 0){  
    6.         if(s1.equals(s3)) return true;  
    7.         else return false;  
    8.       }  
    9.       if(s3.charAt(0) == s1.charAt(0) && s3.charAt(0) != s2.charAt(0)){  
    10.         return isInterleave(s1.substring(1), s2, s3.substring(1));  
    11.       }else if(s3.charAt(0) == s2.charAt(0) && s3.charAt(0) != s1.charAt(0)){  
    12.         return isInterleave(s1, s2.substring(1), s3.substring(1));  
    13.       }else if(s3.charAt(0) == s1.charAt(0) && s3.charAt(0) == s2.charAt(0)){  
    14.         return isInterleave(s1.substring(1), s2, s3.substring(1)) || isInterleave(s1, s2.substring(1), s3.substring(1));  
    15.       }else{  
    16.         return false;  
    17.       }  
    18.   }  


    既然DFS可以,那么BFS也就坐不住了,也要出来浪一波。这里我们需要用队列queue来辅助运算,如果将解法一讲解中的那个二维dp数组列出来的TF图当作一个迷宫的话,那么BFS的目的就是要从(0, 0)位置找一条都是T的路径通到(n1, n2)位置,这里我们还要使用哈希集合,不过此时保存到是已经遍历过的位置,队列中还是存key值,key值的encode方法跟上面DFS解法的相同,初识时放个0进去。然后我们进行while循环,循环条件除了q不为空,还有一个是k小于n3,因为匹配完s3中所有的字符就结束了。然后由于是一层层的遍历,所以要直接循环queue中元素个数的次数,在for循环中,对队首元素进行解码,得到i和j值,如果i小于n1,说明s1还有剩余字符,如果s1当前字符等于s3当前字符,那么把s1的下一个位置i+1跟j一起加码算出key值,如果该key值不在于集合中,则加入集合,同时加入队列queue中;同理,如果j小于n2,说明s2还有剩余字符,如果s2当前字符等于s3当前字符,那么把s2的下一个位置j+1跟i一起加码算出key值,如果该key值不在于集合中,则加入集合,同时加入队列queue中。for循环结束后,k自增1。最后如果匹配成功的话,那么queue中应该只有一个(n1, n2)的key值,且k此时等于n3,所以当queue为空或者k不等于n3的时候都要返回false
    https://discuss.leetcode.com/topic/6562/8ms-c-solution-using-bfs-with-explanation
    If we expand the two strings s1 and s2 into a chessboard, then this problem can be transferred into a path seeking problem from the top-left corner to the bottom-right corner. The key is, each cell (y, x) in the board corresponds to an interval between y-th character in s1 and x-th character in s2. And adjacent cells are connected with like a grid. A BFS can then be efficiently performed to find the path.
    Better to illustrate with an example here:
    Say s1 = "aab" and s2 = "abc". s3 = "aaabcb". Then the board looks like
    o--a--o--b--o--c--o
    |     |     |     |
    a     a     a     a
    |     |     |     |
    o--a--o--b--o--c--o
    |     |     |     |
    a     a     a     a
    |     |     |     |
    o--a--o--b--o--c--o
    |     |     |     |
    b     b     b     b
    |     |     |     |
    o--a--o--b--o--c--o
    
    Each "o" is a cell in the board. We start from the top-left corner, and try to move right or down. If the next char in s3 matches the edge connecting the next cell, then we're able to move. When we hit the bottom-right corner, this means s3 can be represented by interleaving s1 and s2. One possible path for this example is indicated with "x"es below:
    x--a--x--b--o--c--o
    |     |     |     |
    a     a     a     a
    |     |     |     |
    o--a--x--b--o--c--o
    |     |     |     |
    a     a     a     a
    |     |     |     |
    o--a--x--b--x--c--x
    |     |     |     |
    b     b     b     b
    |     |     |     |
    o--a--o--b--o--c--x
    
    Note if we concatenate the chars on the edges we went along, it's exactly s3. And we went through all the chars in s1 and s2, in order, exactly once.
    Therefore if we view this board as a graph, such path finding problem is trivial with BFS. I use an unordered_map to store the visited nodes, which makes the code look a bit complicated. But a vector should be enough to do the job.
    Although the worse case timeis also O(mn), typically it doesn't require us to go through every node to find a path. Therefore it's faster than regular DP than average.
        public boolean isInterleave(String s1, String s2, String s3) {
            if (s1.length() + s2.length() != s3.length()) return false;
            boolean[][] visited = new boolean[s1.length() + 1][s2.length() + 1];
            Queue<int[]> queue = new LinkedList<>();
            queue.offer(new int[]{0, 0});
            while (!queue.isEmpty()) {
                int[] p = queue.poll();
                if (visited[p[0]][p[1]]) continue;
                if (p[0] == s1.length() && p[1] == s2.length()) return true;
                
                if (p[0] < s1.length() && s1.charAt(p[0]) == s3.charAt(p[0] + p[1]))
                    queue.offer(new int[]{p[0] + 1, p[1]});
                if (p[1] < s2.length() && s2.charAt(p[1]) == s3.charAt(p[0] + p[1]))
                    queue.offer(new int[]{p[0], p[1] + 1});
                visited[p[0]][p[1]] = true;
            }
            return false;
        }

    struct MyPoint {
        int y, x; 
        bool operator==(const MyPoint &p) const {
            return p.y == y && p.x == x;
        }
    };
    namespace std {
        template <>
        struct hash<MyPoint> {
            size_t operator () (const MyPoint &f) const {
                return (std::hash<int>()(f.x) << 1) ^ std::hash<int>()(f.y);
            }
        };
    }
    
    class Solution {
    public:
        bool isInterleave(string s1, string s2, string s3) {
            if (s1.size() + s2.size() != s3.size()) return false;
    
            queue<MyPoint> q;
            unordered_set<MyPoint> visited;
            bool isSuccessful = false;
            int i = 0;
    
            q.push(MyPoint { 0, 0 });
            q.push(MyPoint { -1, -1 });
            while (!(1 == q.size() && -1 == q.front().x)) {
                auto p = q.front();
                q.pop();
                if (p.y == s1.size() && p.x == s2.size()) {
                    return true;
                }
                if (-1 == p.y) {
                    q.push(p);
                    i++;
                    continue;
                }
                if (visited.find(p) != visited.end()) { continue; }
                visited.insert(p);
    
                if (p.y < s1.size()) { // down
                    if (s1[p.y] == s3[i]) { q.push(MyPoint { p.y + 1, p.x }); }
                }
                if (p.x < s2.size()) { // right 
                    if (s2[p.x] == s3[i]) { q.push(MyPoint { p.y, p.x + 1 }); }
                }
            }
            return false;
        }
    };
    Imagine a grid, which x-axis and y-axis are s1 and s2, matching s3 is the same as
    finding a path from (0,0) to (len1, len2). It actually becomes a
    BFS on grid. Since we don't need exact paths, a HashSet of
    coordinates is used to eliminate duplicated paths.
        public boolean isInterleave(String s1, String s2, String s3) {
            int len1 = s1.length(),
                len2 = s2.length(),
                len3 = s3.length();
            if (len1 + len2 != len3) return false;
            Deque<Integer> queue = new LinkedList<>();
            int matched = 0;
            queue.offer(0);
            Set<Integer> set = new HashSet<>();
            while (queue.size() > 0 && matched < len3) {
                int size = queue.size();
                for (int i = 0; i < size; i++) {
                    int p1 = queue.peek() / len3,
                        p2 = queue.peek() % len3;
                    queue.poll();
                    if (p1 < len1 && s1.charAt(p1) == s3.charAt(matched)) {
                        int key = (p1 + 1) * len3 + p2;
                        if (!set.contains(key)) {
                            set.add(key);
                            queue.offer(key);
                        }
                    }
                    if (p2 < len2 && s2.charAt(p2) == s3.charAt(matched)) {
                        int key = p1 * len3 + (p2 + 1);
                        if (!set.contains(key)) {
                            set.add(key);
                            queue.offer(key);
                        }
                    }
                }
                matched++;
            }
            return queue.size() > 0 && matched == len3;
        }
    https://discuss.leetcode.com/topic/19900/python-dp-solutions-o-m-n-o-n-space-bfs-dfs/2
    def isInterleave(self, s1, s2, s3):
        r, c, l= len(s1), len(s2), len(s3)
        if r+c != l:
            return False
        queue, visited = [(0, 0)], set((0, 0))
        while queue:
            x, y = queue.pop(0)
            if x+y == l:
                return True
            if x+1 <= r and s1[x] == s3[x+y] and (x+1, y) not in visited:
                queue.append((x+1, y)); visited.add((x+1, y))
            if y+1 <= c and s2[y] == s3[x+y] and (x, y+1) not in visited:
                queue.append((x, y+1)); visited.add((x, y+1))
        return False

    X. DFS + cache
      public boolean is_Interleave(String s1, int i, String s2, int j, String s3, int k, int[][] memo) {
        if (i == s1.length()) {
          return s2.substring(j).equals(s3.substring(k));
        }
        if (j == s2.length()) {
          return s1.substring(i).equals(s3.substring(k));
        }
        if (memo[i][j] >= 0) {
          return memo[i][j] == 1 ? true : false;
        }
        boolean ans = false;
        if (s3.charAt(k) == s1.charAt(i) && is_Interleave(s1, i + 1, s2, j, s3, k + 1, memo)
            || s3.charAt(k) == s2.charAt(j) && is_Interleave(s1, i, s2, j + 1, s3, k + 1, memo)) {
          ans = true;
        }
        memo[i][j] = ans ? 1 : 0;
        return ans;
      }

      public boolean isInterleave(String s1, String s2, String s3) {
        int memo[][] = new int[s1.length()][s2.length()];
        for (int i = 0; i < s1.length(); i++) {
          for (int j = 0; j < s2.length(); j++) {
            memo[i][j] = -1;
          }
        }
        return is_Interleave(s1, 0, s2, 0, s3, 0, memo);

      }
    https://leetcode.com/articles/interleaving-strings/
    Time complexity : O(2^{m+n})m is the length of s1 and n is the length of s2.
      public boolean is_Interleave(String s1, int i, String s2, int j, String res, String s3) {
        if (res.equals(s3) && i == s1.length() && j == s2.length())
          return true;
        boolean ans = false;
        if (i < s1.length())
          ans |= is_Interleave(s1, i + 1, s2, j, res + s1.charAt(i), s3);
        if (j < s2.length())
          ans |= is_Interleave(s1, i, s2, j + 1, res + s2.charAt(j), s3);
        return ans;

      }

      public boolean isInterleave(String s1, String s2, String s3) {
        return is_Interleave(s1, 0, s2, 0, "", s3);

      }

    https://www.cnblogs.com/higerzhang/p/4122234.html
    然后就想用动态规划了,一开始我定义dp[i][j][k],表示s1的前i个和s2的前j个是否和s3的前k个匹配。bool型的。如下:
    后面觉得不要用三维可以,可以把dp的第三维度干掉。dp[i][j]就表示s1的前i个和s2的前j个是否和s3的前i+j个匹配
        bool isInterleave(string s1, string s2, string s3) 
        {
            int len1 = s1.size(), len2 = s2.size(), len3 = s3.size();
            if (len1 == 0) return s2 == s3;
            if (len2 == 0) return s1 == s3;
            if (len1 + len2 != len3) return false;
            bool dp[len1+1][len2+1][len3+1];
            
            for (int i = 1; i <= len3; ++i)
            {
                if (i <= len1)
                    dp[i][0][i] = s1[i-1] == s3[i-1];
                if (i <= len2)
                    dp[0][i][i] = s2[i-1] == s3[i-1];
            }
            
            for (int i = 1; i <= len1; ++i)
                for (int j = 1; j <= len2; ++j)
                {
                    int k = i + j;
                    if (s1[i-1] == s3[k-1] && s1[i-1] != s2[j-1])
                        dp[i][j][k] = dp[i-1][j][k-1];
                    else if (s2[j-1] == s3[k-1] && s1[i-1] != s2[j-1])
                        dp[i][j][k] = dp[i][j-1][k-1];
                    else if (s1[i-1] == s2[j-1] && s1[i-1] == s3[k-1])
                        dp[i][j][k] = (dp[i][j-1][k-1] || dp[i-1][j][k-1]);
                    else 
                        dp[i][j][k] = 0;
                    }
            return dp[len1][len2][len3];
        }
    


    Labels

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

    Popular Posts