Leetcode:Interleaving String 解題報告

Interleaving String
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.

題解:
到了HARD難度了,主頁君好興奮啊!啦啦啦【Leetcode-HARD】【DP】Interleaving <wbr>String <wbr>解題報告

讓咱們來逐層分解DP吧!

其實DP的本質是Bottom -- UP的解決方法,先解決小的子問題,再一步一步解決大的問題,而遞歸是TOP-DOWN的解決方案。一開始就想最頂層的解決方案,經過遞歸來一層層分解成小的子問題。

今天咱們來探討一下本題的3種解法
1. Brute Force的遞歸解法。
index1, index2, index3分別表明s1,s2,s3三個字符串的起始地址(就是從哪裏開始判斷)
判斷s1, s2, s3是不是Interleaving String 應該這樣判斷:
(1). s3的首字母是來自s1嗎?若是是,能夠拆解爲s1的 index1+1 與 s2 index2 與s3的Interleaving String子問題。
(2). s3的首字母是來自s2嗎?若是是,能夠拆解爲s2的 index2+1 與 s1 index1 與s3的Interleaving String子問題。
以上2個條件成立一個,表示咱們的結果是成立。
Base Case:
若是index3 超過了s3的length,表示s3是空串,這時應返回TRUE

代碼主頁君沒有寫,能夠參考第二個解法Brute Force 帶Memory的解法,只須要把最後一個memory的參數拿走就能夠了。

若是直接這樣寫Leetcode是time limited.

2. 遞歸加記憶矩陣的解法。
與1的思路徹底一致,可是咱們加一個二維矩陣來記錄結果值,這樣能夠減小沒必要要的重複計算. 這樣下來速度與DP會徹底一致,也能夠經過Leetcode的檢查
java

 1 // Solution1: Recursion with memory
 2     public static boolean isInterleave1(String s1, String s2, String s3) {
 3         if (s1 == null || s2 == null || s3 == null) {
 4             return false;
 5         }
 6 
 7         int len1 = s1.length();
 8         int len2 = s2.length();
 9         int len3 = s3.length();
10 
11         // The length is not equal, just return false.
12         if (len1 + len2 != len3) {
13             return false;
14         }
15 
16         int[][][] memory = new int[len1 + 1][len2 + 1][len3 + 1];
17         for (int i = 0; i <= len1; i++) {
18             for (int j = 0; j <= len2; j++) {
19                 for (int k = 0; k <= len3; k++) {
20                     memory[i][j][k] = -1;
21                 }
22             }
23         }
24 
25         return recMemory(s1, 0, s2, 0, s3, 0, memory);
26     }
27 
28     public static boolean recMemory(String s1, int index1, String s2,
29             int index2, String s3, int index3, int[][][] memory) {
30         int len1 = s1.length();
31         int len2 = s2.length();
32         int len3 = s3.length();
33 
34         if (index3 == len3 && index1 == len1 && index2 == len2) {
35             return true;
36         }
37 
38         if (memory[index1][index2][index3] != -1) {
39             return memory[index1][index2][index3] == 1;
40         }
41 
42         // 第一個字符,有2種可能:來自s1, 或是來自s2
43         boolean ret = false;
44         if (index1 < len1 && s1.charAt(index1) == s3.charAt(index3)) {
45             ret = recMemory(s1, index1 + 1, s2, index2, s3, index3 + 1, memory);
46         }
47 
48         // 若是不成功(首字母不來自於s1),嘗試另外一種可能
49         if (!ret && index2 < len2 && s2.charAt(index2) == s3.charAt(index3)) {
50             ret = recMemory(s1, index1, s2, index2 + 1, s3, index3 + 1, memory);
51         }
52 
53         memory[index1][index2][index3] = ret ? 1 : 0;
54         return ret;
55     }
56 
57     // Solution2: Recursion with memory
58     // 思考了一下看了一下過去的代碼,發現其實咱們用不到三維數組,由於len1 + len2 = len3,
59     // 因此第三維根本能夠省略嘛
60     public static boolean isInterleave2(String s1, String s2, String s3) {
61         if (s1 == null || s2 == null || s3 == null) {
62             return false;
63         }
64 
65         int len1 = s1.length();
66         int len2 = s2.length();
67         int len3 = s3.length();
68 
69         // The length is not equal, just return false.
70         if (len1 + len2 != len3) {
71             return false;
72         }
73 
74         int[][] memory = new int[len1 + 1][len2 + 1];
75         for (int i = 0; i <= len1; i++) {
76             for (int j = 0; j <= len2; j++) {
77                 memory[i][j] = -1;
78             }
79         }
80 
81         return recMemory2(s1, 0, s2, 0, s3, 0, memory);
82     }
View Code

3. DPgit

D[i][j]: 定義爲s1 (前i個字符) s2(前j個字符) s3(i+j 個字符) 是否是交叉字符
遞推公式: (s1.i == s3.(i+j) && D[i-1][j]) || (s2.j == s3.(i+j) && D[i][j - 1])
 分別從s1,s2兩種可能性來匹配 ,二者有一個成立就好了。
            s1 s3 首字母相同,繼續查i -1 與 i + j -1 是否isInterleave1
            s2 s3 首字母相同,繼續查j -1 與 i + j -1 是否isInterleave1

初始化:D 0,0 就是true,由於都是空.
D[0][j] 就是判斷一下str2與str3是否是尾字符相同,及D[0][j - 1]是否是true
D[i][0] 就是判斷一下str1與str3是否是尾字符相同,及D[i - 1][0]是否是truegithub

 1 public class Solution {
 2     public boolean isInterleave1(String s1, String s2, String s3) {
 3         if (s1 == null || s2 == null || s3 == null) {
 4             return false;
 5         }
 6         
 7         int len1 = s1.length();
 8         int len2 = s2.length();
 9         int len3 = s3.length();
10         
11         if (len1 + len2 != len3) {
12             return false;
13         }
14         
15         boolean[][] D = new boolean[len1 + 1][len2 + 1];
16         
17         for (int i = 0; i <= len1; i++) {
18             for (int j = 0; j <= len2; j++) {
19                 D[i][j] = false;
20                 if (i == 0 && j == 0) {
21                     D[i][j] = true;
22                 } else if (i == 0) {
23                     D[i][j] = s2.charAt(j - 1) == s3.charAt(i + j - 1) && D[i][j - 1];
24                 } else if (j == 0) {
25                     D[i][j] = s1.charAt(i - 1) == s3.charAt(i + j - 1) && D[i - 1][j];
26                 } else {
27                     D[i][j] |= s2.charAt(j - 1) == s3.charAt(i + j - 1) && D[i][j - 1];
28                     D[i][j] |= s1.charAt(i - 1) == s3.charAt(i + j - 1) && D[i - 1][j];
29                 }
30             }
31         }
32         
33         return D[len1][len2];
34     }
35     
36     public boolean isInterleave(String s1, String s2, String s3) {
37         if (s1 == null || s2 == null || s3 == null) {
38             return false;
39         }
40         
41         int len1 = s1.length();
42         int len2 = s2.length();
43         int len3 = s3.length();
44         
45         if (len1 + len2 != len3) {
46             return false;
47         }
48         
49         boolean[][] D = new boolean[len1 + 1][len2 + 1];
50         
51         for (int i = 0; i <= len1; i++) {
52             for (int j = 0; j <= len2; j++) {
53                 D[i][j] = false;
54                 if (i == 0 && j == 0) {
55                     D[i][j] = true;
56                     continue;
57                 }
58                 
59                 if (i != 0) {
60                     D[i][j] |= s1.charAt(i - 1) == s3.charAt(i + j - 1) && D[i - 1][j];
61                 }
62                 
63                 if (j != 0) {
64                     D[i][j] |= s2.charAt(j - 1) == s3.charAt(i + j - 1) && D[i][j - 1];
65                 }
66             }
67         }
68         
69         return D[len1][len2];
70     }
71 }
View Code


總結:同窗在面試的時候,能夠嘗試寫一個遞歸,而後根據當前問題的規模(好比咱們如今是有2個字符串的不一樣長度組合),那麼就能夠建立一個二維矩陣來記錄結果,加在遞歸上就能輕鬆減小複雜度。本題DP/帶記憶的遞歸都是N^2的複雜度。

若是面試官進一步問,你能夠根據前面遞歸的思路,推一個DP公式出來。這個多練幾遍很快就會熟悉了。

最後上代碼:
GitHub代碼連接面試

相關文章
相關標籤/搜索