我试图解决这个问题,我放弃了,并找到了下面的解决方案,虽然我不明白解决方案是如何工作的,或者为什么它有效。任何深入的解决方案都将深受赞赏。
问题:
鉴于
s1
,s2
,s3
,查找s3
是否由s1
的交错形成 和s2
。例如,鉴于:
s1 = "aabcc" s2 = "dbbca"
- 当
s3 = "aadbbcbcac"
时,返回true。- 当
s3 = "aadbbbaccc"
时,返回false。
解决方案:
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()];
}
答案 0 :(得分:4)
我在这里使用Python切片语法因为它很好。 S[:-1]
表示删除了最后一个字符的字符串S
,S[:n]
表示长度为S
的{{1}}前缀。
关键的想法是,如果n
是C
和A
的交错,则B
是C[:-1]
和{{1}的交错}或A
和B[:-1]
。另一方面,如果A[:-1]
是B
和C
的交错,则A
是B
和C + 'X'
的交错。也是A + 'X'
和B
的交错。
这些是我们应用动态编程所需的子结构属性。
我们定义A
,如果B + 'X'
和f(i, j) = true
可以交错形成s1[:i]
和s2[:j]
。通过上面的观察我们有
s3[:(i+j)]
在所有其他情况下,我们有f(i,j) = false
。
Java代码使用动态编程实现此重复。也就是说,我会以更简洁的方式实现它:
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)
答案 1 :(得分:2)
前几行只处理可以仅根据输入字符串长度解决的简单情况。这些测试通过允许假设长度匹配来简化剩余代码。
算法的内核计算数组isInter
,其中isInter[i][j]
在迭代(i,j)
之后为真,当且仅当{{1}的i+j
个字符时可以通过将s3
的第一个i
字符与s1
的第一个j
字符进行交错来形成。
s2
的{{1}}个字符与isInter[i][0]
的第一个i
字符匹配时, s3
才有效。同样,如果且仅当i
的{{1}}个字符与s1
的第一个isInter[0][i]
字符匹配时,i
才为真。
最终循环使用已计算的元素构建isInter的其余元素,并匹配s3
的下一个字符与i
的下一个字符或s2
的下一个字符之间的匹配
完全计算s3
后,s1
为真,如果且仅当整个s2
可以通过将整个isInter
与整个isInter[s1.length()][s2.length()]
。
答案 2 :(得分:0)
这是另一种尝试,使用C ++中非常简单的语法来展示代码的工作原理。它们是代码中的一些内联注释,供您理解。它不能比这简单:)
//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;
}