力扣LeetCode #97 交错字符串(IsInterleave)

本文介绍了一种使用动态规划方法来验证两个字符串是否可以交错组成第三个字符串的问题。通过具体示例和JAVA代码实现,展示了如何判断s3是否由s1和s2交错而成。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

- 题目描述

给定三个字符串 s1、s2、s3,请你帮忙验证 s3 是否是由 s1 和 s2 交错组成的。两个字符串 s 和 t交错的定义与过程如下,其中每个字符串都会被分割成若干 非空 子字符串:

  • s = s1 + s2 + … + sn
  • t = t1 + t2 + … + tm
  • |n - m| <= 1
  • 交错 是 s1 + t1 + s2 + t2 + s3 + t3 + … 或者 t1 + s1 + t2 + s2 + t3 + s3 + …

提示:

  • 0 <= s1.length, s2.length <= 100
  • 0 <= s3.length <= 200
  • s1、s2、和 s3 都由小写英文字母组成

来源:LeetCode

- 示例

  • 示例 1:
    输入:s1 = “aabcc”, s2 = “dbbca”, s3 = “aadbbcbcac”
    输出:true
  • 示例 2:
    输入:s1 = “aabcc”, s2 = “dbbca”, s3 = “aadbbbaccc”
    输出:false
  • 示例 3:
    输入:s1 = “”, s2 = “”, s3 = “”
    输出:true

- 思路分析

  • 动态规划方法。在我意识到自己用的是动态规划之前我的代码已经通过了,就是有点慢,返回去看怎么改进才发现是动态规划。
  • 动态规划已经遇到好多题了。假设字符串s1,s2,s3和i,j, i表示s1当前字符的位置,j表示s2当前字符的位置,那么需要考虑的就是s1[i], s2[j], s3[i+j](实际上字符串不能这样用,这里为了简单就这样写了)。定义 m a t c h [ i ] [ j ] match[i][j] match[i][j],表示s1[:,i]和s2[:,j]是否能够表示s3[:,i+j](这里借用一下python的字符串截取方法,s[:,i]表示s从头开始到位置i的子串)。那么本题的状态转换方程式:
    m a t c h [ i ] [ j ] = { m a t c h [ i − 1 ] [ j ] , i f ( s 1 [ i ] = = s 3 [ i + j ] ) m a t c h [ i ] [ j − 1 ] , i f ( s 2 [ i ] = = s 3 [ i + j ] ) match[i][j]=\left\{ \begin{aligned} match[i-1][j], if(s1[i] == s3[i+j])\\ match[i][j-1], if(s2[i] == s3[i+j]) \end{aligned} \right. match[i][j]={match[i1][j],if(s1[i]==s3[i+j])match[i][j1],if(s2[i]==s3[i+j])
  • 然后就是注意一下边界判断就行了。动态规划有两种实现方法:自底向上和自顶向下,这里写了两种自底向上的方法:memoization和普通递归。

- JAVA实现

  • 自底向上:memoization(通过存储计算过的结果,使递归更高效)
class Solution {
    public boolean isInterleave(String s1, String s2, String s3) {
        if(s1.length() == 0) return s2.equals(s3);
        if(s2.length() == 0) return s1.equals(s3);
        if(s1.length()+s2.length()!=s3.length()) return false;
        boolean[][] match = new boolean[s1.length()+1][s2.length()+1];
        //边界处理
        match[0][0] = true;
        for(int j=1; j<s2.length()+1; j++) {
            if(s2.substring(0,j).equals(s3.substring(0,j))) match[0][j] = match[0][j-1] && true;
            else match[0][j] = false;
        }
        for(int j=1; j<s1.length()+1; j++) {
            if(s1.substring(0,j).equals(s3.substring(0,j))) match[j][0] = match[j-1][0] && true;
            else match[j][0] = false;
        }
        //开始递归
        for(int i=1; i<s1.length()+1; i++) {
            for(int j=1; j<s2.length()+1; j++) {
                match[i][j] = (match[i-1][j] && s1.charAt(i-1) == s3.charAt(i+j-1)) || (match[i][j-1] && s2.charAt(j-1) == s3.charAt(i+j-1));
            }
        }
        return match[s1.length()][s2.length()];
    }
}
  • 自底向上:普通递归,自己调用自己,代码要简单一点
class Solution {
    public boolean isInterleave(String s1, String s2, String s3) {
        if(s1.length() == 0) return s2.equals(s3);
        if(s2.length() == 0) return s1.equals(s3);
        if(s1.length()+s2.length()!=s3.length()) return false;
        return check(s1, s2, s3, 0, 0, 0);
    }
    
    public boolean check(String s1, String s2, String s3, int i, int j, int pos) {
    	//i、j、pos分别对应当前s1、s2、s3的位置
    	//base case
        if(i==s1.length() && j == s2.length() && pos == s3.length()) return true;
        boolean match1 = false, match2 = false;
        if(i<s1.length() && s3.charAt(pos) == s1.charAt(i)) {
            match1 = check(s1, s2, s3, i+1, j, pos+1);
        }
        if(match1 == true) return match1; 
        //这里判断是为了减少递归次数,如果已经发现匹配了,就不考虑其他可能匹配了。
        if(j<s2.length() && s3.charAt(pos) == s2.charAt(j)) {
            match2 = check(s1, s2, s3, i, j+1, pos+1);
        }
        if(match2 == true) return match2;
        else return false;
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值