问题
I was trying to solve this problem, and I gave up, and found the solution below, although I do not understand how the solution works, or why it works. Any in-depth solution would be deeply appreciated.
Question:
Given
s1
,s2
,s3
, find whethers3
is formed by the interleaving ofs1
ands2
.For example, Given:
s1 = "aabcc" s2 = "dbbca"
- When
s3 = "aadbbcbcac"
, return true.- When
s3 = "aadbbbaccc"
, return false.
Solution:
public static boolean isInterleave(String s1, String s2, String s3) {
if (s3.length() == 0 && s1.length() == 0 && s2.length() == 0)
return true;
else if (s3.length() != s1.length() + s2.length())
return false;
boolean isInter[][] = new boolean[s1.length()+1][s2.length()+1];
isInter[0][0] = true;
for ( int i = 1; i <= s1.length(); ++i){
if (s1.charAt(i-1) == s3.charAt(i-1))
isInter[i][0] = true;
else
break;
}
for ( int i = 1; i <= s2.length(); ++i){
if (s2.charAt(i-1) == s3.charAt(i-1))
isInter[0][i] = true;
else
break;
}
// DP
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))
isInter[i][j] = isInter[i-1][j] || isInter[i][j];
if (s3.charAt(i+j-1) == s2.charAt(j-1))
isInter[i][j] = isInter[i][j-1] || isInter[i][j];
}
}
return isInter[s1.length()][s2.length()];
}
回答1:
I'm using Python slice syntax here because it is nice. S[:-1]
means the string S
with its last character removed and S[:n]
means the prefix of S
that has length n
.
The key idea is that if C
is an interleaving of A
and B
, then C[:-1]
is either an interleaving of A
and B[:-1]
or of A[:-1]
and B
. On the other hand, if C
is an interleaving of A
and B
, then C + 'X'
is an interleaving of A + 'X'
and B
and it is also an interleaving of A
and B + 'X'
.
These are the substructure properties we need to apply dynamic programming.
We define f(i, j) = true
, if s1[:i]
and s2[:j]
can be interleaved to form s3[:(i+j)]
and f(i,j) = false
otherwise. By the observation above we have
f(0,0) = true
f(i,0) = f(i-1, 0) if s3[i-1] == s1[i-1]
f(0,j) = f(0, j-1) if s3[j-1] == s2[j-1]
f(i,j) = true if s3[i+j-1] = s1[i-1] and f(i-1, j)
f(i,j) = true if s3[i+j-1] = s2[j-1] and f(i, j-1)
In all the other cases we have f(i,j) = false
.
The Java code just implements this recurrence using dynamic programming. That said, I would implement it in a bit more concise way:
for (int i = 0; i <= s1.length(); ++i)
for (int j = 0; j <= s2.length(); ++j)
isInter[i][j] = (i == 0 && j == 0)
|| (i > 0 && s1.charAt(i-1) == s3.charAt(i+j-1) && isInter[i-1][j])
|| (j > 0 && s2.charAt(j-1) == s3.charAt(i+j-1) && isInter[i][j-1]);
回答2:
The first few lines just deal with simple cases that can be resolved based only on the input string lengths. These tests simplify the remaining code by allowing it to assume that the lengths match.
The guts of the algorithm calculate an array isInter
, where isInter[i][j]
is true after iteration (i,j)
if, and only if, the first i+j
characters of s3
can be formed by interleaving the first i
characters of s1
with the first j
characters of s2
.
isInter[i][0]
is true if, and only if, the first i
characters of s3
match the first i
characters of s1
. Similarly, isInter[0][i]
is true if, and only if, the first i
characters of s3
match the first i
characters of s2
.
The final loop builds up the remaining elements of isInter using already calculated elements and matches between the next character of s3
and either the next character of s1
or the next character of s2
.
After isInter
has been completely calculated, isInter[s1.length()][s2.length()]
is true if, and only if, the whole of s3
can be formed by interleaving the whole of s1
with the whole of s2
.
回答3:
Here is another attempt to show how the code works, using extremely plain syntax in C++. Their are some inline comments in the code for you to understand. It can't get simpler than this :)
//using dynamic programming : O(n^2)
bool is_interleaved_On2_DP(string s1, string s2, string s3)
{
bool ret = false;
int len1 = s1.length();
int len2 = s2.length();
int len3 = s3.length();
if( len1 + len2 != len3 )
{
return ret;
}
#define M(i, j) match[ (i) * len1 + (j) ]
//bool match[len1 + 1][len2 + 1]; //todo; use dynamic allocation
bool* match = new bool[ (len1+1)*(len2+1) ];
int i, j, k;
/* init match table to be all false */
for( i = 0; i <= len1; i++ )
{
for( j = 0; j <= len2; j++ )
{
M(i, j) = false;
}
}
/* init match[0][0] == true */
M(0, 0) = true; //why ? null is interleaving 2 other nulls :)
/* init the s1 side of table i.e. column */
for( i = 1; i <= len1; i++ )
{
char c1 = s1[i - 1];
char c3 = s3[i - 1];
if(c1 == c3)
{
M(i, 0) = true;
}
else
{
break;
}
}
/* init the s2 side of table i.e. row */
for( j = 1; j <= len2; j++ )
{
char c2 = s2[j - 1];
char c3 = s3[j - 1];
if(c2 == c3)
{
M(0, j) = true;
}
else
{
break;
}
}
/* compute remaining table */
for( i = 1; i <= len1; i++ )
{
char c1 = s1[i - 1];
for( j = 1; j <= len2; j++ )
{
char c2 = s2[j - 1];
int k = i + j; //index for s3
char c3 = s3[k - 1];
if(c1 == c3)
{
M(i, j) = M(i - 1, j) || M(i, j);
}
if(c2 == c3)
{
M(i, j) = M(i, j - 1) || M(i, j);
}
}
}
ret = M(len1, len2);
delete [] match;
#undef M
return ret;
}
来源:https://stackoverflow.com/questions/22795589/dynamic-programming-problems-solution-to-interleaving-strings