交错字符串的动态编程问题解决方案

时间:2014-04-01 19:52:08

标签: java algorithm

我试图解决这个问题,我放弃了,并找到了下面的解决方案,虽然我不明白解决方案是如何工作的,或者为什么它有效。任何深入的解决方案都将深受赞赏。

问题:

  

鉴于s1s2s3,查找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()];
    }

3 个答案:

答案 0 :(得分:4)

我在这里使用Python切片语法因为它很好。 S[:-1]表示删除了最后一个字符的字符串SS[:n]表示长度为S的{​​{1}}前缀。

关键的想法是,如果nCA的交错,则BC[:-1]和{{1}的交错}或AB[:-1]。另一方面,如果A[:-1]BC的交错,则ABC + '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;
    }