给定三个字符串 s1、s2、s3,请你帮忙验证 s3 是否是由 s1 和 s2 __交错  组成的。
两个字符串 s 和 t 交错  的定义与过程如下,其中每个字符串都会被分割成若干 非空  子字符串:
s = s1 + s2 + ... + snt = t1 + t2 + ... + tm|n - m| <= 1交错  是 s1 + t1 + s2 + t2 + s3 + t3 + ... 或者 t1 + s1 + t2 + s2 + t3 + s3 + ... 
注意: a + b 意味着字符串 a 和 b 连接。
示例 1: 
**输入:** s1 = "aabcc", s2 = "dbbca", s3 = "aadbbcbcac"
**输出:** true
示例 2: 
**输入:** s1 = "aabcc", s2 = "dbbca", s3 = "aadbbbaccc"
**输出:** false
示例 3: 
**输入:** s1 = "", s2 = "", s3 = ""
**输出:** true
提示: 
0 <= s1.length, s2.length <= 1000 <= s3.length <= 200s1、s2、和 s3 都由小写英文字母组成 
进阶:  您能否仅使用 O(s2.length) 额外的内存空间来解决它?
方法一:动态规划 记 $|s_1| = n$,$|s_2| = m$。
思路与算法 
双指针法错在哪里?  也许有同学看到这道题目的第一反应是使用双指针法解决这个问题,指针 $p_1$ 一开始指向 $s_1$ 的头部,指针 $p_2$ 一开始指向 $s_2$ 的头部,指针 $p_3$ 指向 $s_3$ 的头部,每次观察 $p_1$ 和 $p_2$ 指向的元素哪一个和 $p_3$ 指向的元素相等,相等则匹配并后移指针。样例就是一个很好的反例,用这种方法判断 $s_1 = {\rm aabcc}$,$s_2 = {\rm dbbca}$,$s_3 = {\rm aadbbcbcac}$ 时,得到的结果是 $\rm False$,实际应该是 $\rm True$。
解决这个问题的正确方法是动态规划。  首先如果 $|s_1| + |s_2| \neq |s_3|$,那 $s_3$ 必然不可能由 $s_1$ 和 $s_2$ 交错组成。在 $|s_1| + |s_2| = |s_3|$ 时,我们可以用动态规划来求解。我们定义 $f(i, j)$ 表示 $s_1$ 的前 $i$ 个元素和 $s_2$ 的前 $j$ 个元素是否能交错组成 $s_3$ 的前 $i + j$ 个元素。如果 $s_1$ 的第 $i$ 个元素和 $s_3$ 的第 $i + j$ 个元素相等,那么 $s_1$ 的前 $i$ 个元素和 $s_2$ 的前 $j$ 个元素是否能交错组成 $s_3$ 的前 $i + j$ 个元素取决于 $s_1$ 的前 $i - 1$ 个元素和 $s_2$ 的前 $j$ 个元素是否能交错组成 $s_3$ 的前 $i + j - 1$ 个元素,即此时 $f(i, j)$ 取决于 $f(i - 1, j)$,在此情况下如果 $f(i - 1, j)$ 为真,则 $f(i, j)$ 也为真。同样的,如果 $s_2$ 的第 $j$ 个元素和 $s_3$ 的第 $i + j$ 个元素相等并且 $f(i, j - 1)$ 为真,则 $f(i, j)$ 也为真。于是我们可以推导出这样的动态规划转移方程:
$$f(i, j) = [f(i - 1, j) , {\rm and} , s_1(i - 1) = s_3(p)] , {\rm or} , [f(i, j - 1) , {\rm and} , s_2(j - 1) = s_3(p)]$$
其中 $p = i + j - 1$。边界条件为 $f(0, 0) = {\rm True}$。至此,我们很容易可以给出这样一个实现:
[sol0-C++] 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 class  Solution  {public :    bool  isInterleave (string s1, string s2, string s3)           auto  f = vector < vector <int > > (s1.size () + 1 , vector <int > (s2.size () + 1 , false ));         int  n = s1.size (), m = s2.size (), t = s3.size ();         if  (n + m != t) {             return  false ;         }         f[0 ][0 ] = true ;         for  (int  i = 0 ; i <= n; ++i) {             for  (int  j = 0 ; j <= m; ++j) {                 int  p = i + j - 1 ;                 if  (i > 0 ) {                     f[i][j] |= (f[i - 1 ][j] && s1[i - 1 ] == s3[p]);                 }                 if  (j > 0 ) {                     f[i][j] |= (f[i][j - 1 ] && s2[j - 1 ] == s3[p]);                 }             }         }         return  f[n][m];     } }; 
[sol0-Java] 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 class  Solution  {    public  boolean  isInterleave (String s1, String s2, String s3)  {         int  n  =  s1.length(), m = s2.length(), t = s3.length();         if  (n + m != t) {             return  false ;         }         boolean [][] f = new  boolean [n + 1 ][m + 1 ];         f[0 ][0 ] = true ;         for  (int  i  =  0 ; i <= n; ++i) {             for  (int  j  =  0 ; j <= m; ++j) {                 int  p  =  i + j - 1 ;                 if  (i > 0 ) {                     f[i][j] = f[i][j] || (f[i - 1 ][j] && s1.charAt(i - 1 ) == s3.charAt(p));                 }                 if  (j > 0 ) {                     f[i][j] = f[i][j] || (f[i][j - 1 ] && s2.charAt(j - 1 ) == s3.charAt(p));                 }             }         }         return  f[n][m];     } } 
[sol0-Golang] 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 func  isInterleave (s1 string , s2 string , s3 string ) bool  {    n, m, t := len (s1), len (s2), len (s3)     if  (n + m) != t {         return  false      }     f := make ([][]bool , n + 1 )     for  i := 0 ; i <= n; i++ {         f[i] = make ([]bool , m + 1 )     }     f[0 ][0 ] = true      for  i := 0 ; i <= n; i++ {         for  j := 0 ; j <= m; j++ {             p := i + j - 1              if  i > 0  {                 f[i][j] = f[i][j] || (f[i-1 ][j] && s1[i-1 ] == s3[p])             }             if  j > 0  {                 f[i][j] = f[i][j] || (f[i][j-1 ] && s2[j-1 ] == s3[p])             }         }     }     return  f[n][m] } 
[sol0-C] 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 bool  isInterleave (char * s1, char * s2, char * s3)  {    int  n = strlen (s1), m = strlen (s2), t = strlen (s3);     int  f[n + 1 ][m + 1 ];     memset (f, 0 , sizeof (f));     if  (n + m != t) {         return  false ;     }     f[0 ][0 ] = true ;     for  (int  i = 0 ; i <= n; ++i) {         for  (int  j = 0 ; j <= m; ++j) {             int  p = i + j - 1 ;             if  (i > 0 ) {                 f[i][j] |= (f[i - 1 ][j] && s1[i - 1 ] == s3[p]);             }             if  (j > 0 ) {                 f[i][j] |= (f[i][j - 1 ] && s2[j - 1 ] == s3[p]);             }         }     }     return  f[n][m]; } 
不难看出这个实现的时间复杂度和空间复杂度都是 $O(nm)$。
使用滚动数组优化空间复杂度。  因为这里数组 $f$ 的第 $i$ 行只和第 $i - 1$ 行相关,所以我们可以用滚动数组优化这个动态规划,这样空间复杂度可以变成 $O(m)$。敲黑板:我们又遇到「滚动数组」优化啦!不会的同学一定要学习哟。如果还没有做过这几个题建议大家做一下,都可以使用这个思想进行优化: 
下面给出滚动数组优化的代码。
代码 
[sol1-C++] 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 class  Solution  {public :    bool  isInterleave (string s1, string s2, string s3)           auto  f = vector <int > (s2.size () + 1 , false );         int  n = s1.size (), m = s2.size (), t = s3.size ();         if  (n + m != t) {             return  false ;         }         f[0 ] = true ;         for  (int  i = 0 ; i <= n; ++i) {             for  (int  j = 0 ; j <= m; ++j) {                 int  p = i + j - 1 ;                 if  (i > 0 ) {                     f[j] &= (s1[i - 1 ] == s3[p]);                 }                 if  (j > 0 ) {                     f[j] |= (f[j - 1 ] && s2[j - 1 ] == s3[p]);                 }             }         }         return  f[m];     } }; 
[sol1-Java] 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 class  Solution  {    public  boolean  isInterleave (String s1, String s2, String s3)  {         int  n  =  s1.length(), m = s2.length(), t = s3.length();         if  (n + m != t) {             return  false ;         }         boolean [] f = new  boolean [m + 1 ];         f[0 ] = true ;         for  (int  i  =  0 ; i <= n; ++i) {             for  (int  j  =  0 ; j <= m; ++j) {                 int  p  =  i + j - 1 ;                 if  (i > 0 ) {                     f[j] = f[j] && s1.charAt(i - 1 ) == s3.charAt(p);                 }                 if  (j > 0 ) {                     f[j] = f[j] || (f[j - 1 ] && s2.charAt(j - 1 ) == s3.charAt(p));                 }             }         }         return  f[m];     } } 
[sol1-Golang] 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 func  isInterleave (s1 string , s2 string , s3 string ) bool  {    n, m, t := len (s1), len (s2), len (s3)     if  (n + m) != t {         return  false      }     f := make ([]bool , m + 1 )     f[0 ] = true      for  i := 0 ; i <= n; i++ {         for  j := 0 ; j <= m; j++ {             p := i + j - 1              if  i > 0  {                 f[j] = f[j] && s1[i-1 ] == s3[p]             }             if  j > 0  {                 f[j] = f[j] || f[j-1 ] && s2[j-1 ] == s3[p]             }         }     }     return  f[m] } 
[sol1-C] 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 bool  isInterleave (char * s1, char * s2, char * s3)  {    int  n = strlen (s1), m = strlen (s2), t = strlen (s3);     int  f[m + 1 ];     memset (f, 0 , sizeof (f));     if  (n + m != t) {         return  false ;     }     f[0 ] = true ;     for  (int  i = 0 ; i <= n; ++i) {         for  (int  j = 0 ; j <= m; ++j) {             int  p = i + j - 1 ;             if  (i > 0 ) {                 f[j] &= (s1[i - 1 ] == s3[p]);             }             if  (j > 0 ) {                 f[j] |= (f[j - 1 ] && s2[j - 1 ] == s3[p]);             }         }     }     return  f[m]; } 
复杂度分析 
时间复杂度:$O(nm)$,两重循环的时间代价为 $O(nm)$。 
空间复杂度:$O(m)$,即 $s_2$ 的长度。