`
frank-liu
  • 浏览: 1684011 次
  • 性别: Icon_minigender_1
  • 来自: 北京
社区版块
存档分类
最新评论

leetcode: Interleaving String

 
阅读更多

问题描述:

Given s1s2s3, 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.

原问题链接:https://leetcode.com/problems/interleaving-string/

 

问题分析

  这个问题有一点复杂,主要在于要理清楚里面比较复杂的关系。在这里对于两个给定的字符串s1和s2来说,需要轮流从两个串里取一部分字符串作为合并后的串的一部分。因此,从通常的角度来看,必然是首先从一个串里取一截加入到合并的串里。那么具体该取哪个串呢?如果是从串的最开头,那么我们可以既取s1也取s2。而在中间的某个部分的时候,我们则需要交替的取了。

  因此,为了解决这个问题,我们有两种思路来解决。

 

递归法

   在开始的讨论里,我们已经看到,对于比较字段的匹配,我们可以是先取s1和目标串进行比较匹配,也可以开始是取s2来进行匹配。一旦某一个选定之后,我们就只能依次的选取后面的进行匹配了。所以假设在某个时候s1的当前位置是l1, s2的当前位置是l2, s3的当前位置是l3。那么我们就需要有一个元素来标识当前是已经匹配了哪个串的,到底是s1还是s2。这样就可以选择对应的串来和s3比较。

  假定我们已经选定了某个串了,那么就要对它和s3进行比较。这个比较是满足什么样的关系呢?首先一个肯定是当前的字段要匹配。也就是说,假设当前s1位置是l1, s3的位置是l3,那么必然从l1到s1的末尾以及l3到s3的某位有一段是匹配的,可以匹配有1个或者多个元素。如果一个都不匹配,那么就应该返回false。

  按照这个思路,我们得到如下的代码:

 

public class Solution {
    public boolean isInterleave(String s1, String s2, String s3) {
        if(s1.length() + s2.length() != s3.length()) return false;
        return isInterleave(s1, 0, s2, 0, s3, 0, true) || isInterleave(s1, 0, s2, 0, s3, 0, false);
    }
    
    public boolean isInterleave(String s1, int l1, String s2, int l2, String s3, int l3, boolean selected) {
        if(l1 == s1.length()) return s2.substring(l2).equals(s3.substring(l3));
        if(l2 == s2.length()) return s1.substring(l1).equals(s3.substring(l3));
        if(selected) {
            for(int i = 0; i < s1.length() - l1 && s1.charAt(l1 + i) == s3.charAt(l3 + i); i++) {
                if(isInterleave(s1, l1 + i + 1, s2, l2, s3, l3 + i + 1, false)) return true;
            }
        } else {
            for(int i = 0; i < s2.length() - l2 && s2.charAt(l2 + i) == s3.charAt(l3 + i); i++) {
                if(isInterleave(s1, l1, s2, l2 + i + 1, s3, l3 + i + 1, true)) return true;
            }
        }
        return false;
    }
}

  这种递归实现的方式效率并不高,因为它需要针对里面匹配的每个情况进行递归的看,其时间复杂度在极端情况下达到了O(2 ** n)。 所以在实际中运行的时候会导致超时的错误。

  所以,这种办法虽然逻辑上正确,但是效率需要改进。 

 

动态规划法

  在前面递归方法的基础上,我们会很自然的想到要用动态规划这个办法来改进效率。在具体的实现里,我们定义一个boolean[m + 1][n + 1]的矩阵。其中m, n分别表示s1, s2的长度。这样矩阵里boolean[i][j]则分别表示当s1里i位置和s2里j位置时对应s3里i + j位置的匹配情况。

  在i == 0或者j == 0的情况下,相当于单独的s1或者s2和s3进行比较匹配的情况。所以从递归的关系来说matrix[i][j] = matrix[i][j - 1] && s2.charAt(j - 1) == s3.charAt(i + j - 1) 或者

matrix[i][j] = matrix[i- 1][j] && s1.charAt(i - 1) == s3.charAt(i + j - 1)。

  而对于其他的情况呢,则是这两种情况取或的关系。最终结果是返回matrix[m][n]。

  根据上述讨论,详细的实现如下: 

 

public class Solution {
    public boolean isInterleave(String s1, String s2, String s3) {
        if(s3.length() != s1.length() + s2.length()) return false;
        int m = s1.length(), n = s2.length();
        boolean[][] matrix = new boolean[m + 1][n + 1];
        for(int i = 0; i <= m; i++)
            for(int j = 0; j <= n; j++) {
                if(i == 0 && j == 0) matrix[i][j] = true;
                else if(i == 0) matrix[i][j] = matrix[i][j - 1] && s2.charAt(j - 1) == s3.charAt(i + j - 1);
                else if(j == 0) matrix[i][j] = matrix[i - 1][j] && s1.charAt(i - 1) == s3.charAt(i + j - 1);
                else
                    matrix[i][j] = (matrix[i][j-1] && s2.charAt(j - 1) == s3.charAt(i + j - 1)) ||
                        (matrix[i - 1][j] && s1.charAt(i - 1) == s3.charAt(i + j - 1));
            }
        return matrix[m][n];
    }
}

 

分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics