LeetCode:97. 交错字符串

1. 题目描述

给定三个字符串 s1s2s3, 验证 s3 是否是由 s1 和 s2 交错组成的。

示例 1:

示例 2:

2. 解题思路

  s1 , s2 组成 s3 过程中不能改变 s1, s2 的字符顺序,示例2中 s1, s2 字符和与 s3 相同,但由于不能改变s1, s2 的字符顺序,结果为 false.

90%的字符串问题可由动态规划解决,本题就是一道动态规划问题。

首先找出状态转移方程:

假设 dp[i][j] 代表 s1 的前 j 个字符与 s2 的前 i 个字符能否交错组成 s3 的 前 (i + j) 个字符。

如果为True,又代表着 s1 的第 j 个字符或者 s2 的第 i 个字符与 s3 的第(i + j)个字符相等。

接下来找出状态转移方程:

     dp[i][j] = 1, s1.charAt(j-1) == s3.charAt(i+j-1) && dp[i][j-1] == 1;

     dp[i][j] = 1, s2.charAt(i-1) == s3.charAt(i+j-1) && dp[i-1][j] == 1;

     其余情况都为0;

    即当 s1 的第 j 个字符或者 s2 的第 i 个字符与 s3 的第(i + j)个字符相等,并且 dp[i][j-1] 或者 dp[i-1][j] 等于 1.

   dp[i][j-1] 或者 dp[i-1][j]等于 1 代表着能交错组成s3 的 前 (i + j-1) 个字符。如果s1 的第 j 个字符与 s3 的第(i + j)个字符

   相等,只需能交错组成s3 的 前 (i + j-1) 个字符,由于 s1 的第 j 个字符用于组成s3 的第(i + j)个字符,所以需要dp[i][j-1] 为 1. 

   同理s2 的第 i 个字符与 s3 的第(i + j)个字符相等需要dp[i-1][j] 为 1.

   为方便理解以示例1数据填表:

   s3 为 a a d b b c b c a c

   先填边界,即单一字符串匹配情况

坐标   0 1 2 3 4 5
    s1 a a b c c
0 s2   1 1 0 0 0
1 d 0          
2 b 0          
3 b 0          
4 c 0          
5 a 0          

 接下来先从s3的前1个字符开始,需填(0,1),(1,0),由上图可知在边界赋值时已完成,接下来考虑s3的前两个字符,需填坐标(2,0)(0,2)(1,1)。(2,0),(0,2)已完成,填坐标(1,1):判断 s1 第1个或者 s2 第1个是否与 s3 第2个相等。s3 第二个为a,所以s1 第1个与之相等。接下  来还需判断是否能组成 s3 的第 1 个字符,与(1,1)相邻坐标相加为1的有(1,0),(0,1),由于s1第1个被用来与 s3 第2个匹配,所以只能判断(1,0),由于dp[1][0]为0,所以不满足条件,dp[1][1]为0:

坐标   0 1 2 3 4 5
    s1 a a b c c
0 s2   1 1 0 0 0
1 d 0 0        
2 b 0          
3 b 0          
4 c 0          
5 a 0          

 按照转移方程填表,从s3的子串开始:

 s3前1个字符,填(1,0)(0,1)

 s3前2个字符,填 (2,0)(0,2)(1,1)

 s3前3个字符,填 (3,0)(0,3)(2,1)(1,2)

 s3前4个字符,填 (4,0)(0,4)(1,3)(3,1)(2,2)

 s3前5个字符,填 (5,0)(0,5)(2,3)(3,2)(1,4)(4,1)

 s3前6个字符,填 (5,1)(1,5)(3,3)(2,4)(4,2)

 s3前7个字符,填 (2,5)(5,2)(3,4)(4,3)

 s3前8个字符,填 (3,5)(5,3)(4,4)

 s3前9个字符,填 (4,5)(5,4)

 s3前10个字符,填 (5,5)

 由于s1,s2长度限制,有些组合情况没有,如(6,0),(1,7)等,编程时需注意。 

最终可得:

坐标   0 1 2 3 4 5
    s1 a a b c c
0 s2   1 1 0 0 0
1 d 0 0 1 1 0 0
2 b 0 0 1 1 1 0
3 b 0 0 1 0 1 1
4 c 0 0 1 1 1 0
5 a 0 0 0 0 1 1

3. 示例代码

class Solution {
    public boolean isInterleave(String s1, String s2, String s3) {
        int length = s3.length();
        // 特殊情况处理
        if(s1.isEmpty() && s2.isEmpty() && s3.isEmpty()) return true;
        if(s1.isEmpty()) return s2.equals(s3);
        if(s2.isEmpty()) return s1.equals(s3);
        if(s1.length() + s2.length() != length) return false;

        int[][] dp = new int[s2.length()+1][s1.length()+1];
        // 边界赋值
        for(int i = 1;i < s1.length()+1;i++){
            if(s1.substring(0,i).equals(s3.substring(0,i))){
                dp[0][i] = 1;
            }
        }
        for(int i = 1;i < s2.length()+1;i++){
            if(s2.substring(0,i).equals(s3.substring(0,i))){
                dp[i][0] = 1;
            }
        }
        
        for(int i = 2;i <= length;i++){
            // 遍历 i 的所有组成(边界除外)
            for(int j = 1;j < i;j++){
                // 防止越界
                if(s1.length() >= j && i-j <= s2.length()){
                    if(s1.charAt(j-1) == s3.charAt(i-1) && dp[i-j][j-1] == 1){
                        dp[i-j][j] = 1;
                    }
                }
                // 防止越界
                if(s2.length() >= j && i-j <= s1.length()){
                    if(s2.charAt(j-1) == s3.charAt(i-1) && dp[j-1][i-j] == 1){
                        dp[j][i-j] = 1;
                    }
                }
            }
        }
        return dp[s2.length()][s1.length()]==1;
    }
}

 

你可能感兴趣的:(算法)