首页 简历|笔试面试
您现在的位置:百分网 > 职场 > 笔试面试 > LeetCode

97.交错字符串

  • 25年9月4日 发布
  • 551.65KB 共5页
97.交错字符串97.交错字符串97.交错字符串97.交错字符串97.交错字符串

97. 交错字符串

题意

给定三个字符串 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 + ...

注意:a + b 意味着字符串 a 和 b 连接。

难度

中等

示例

示例 1:

097.%E4%BA%A4%E9%94%99%E5%AD%97%E7%AC%A6%E4%B8%B2-

20231106163826.png

输入:s1 = "aabcc", s2 = "dbbca", s3 = "aadbbcbcac"

输出:true

示例 2:

输入:s1 = "aabcc", s2 = "dbbca", s3 = "aadbbbaccc"

输出:false

示例 3:

输入:s1 = "", s2 = "", s3 = ""

输出:true

分析

可能有些小伙伴一看到字符串的问题,很容易想到暴力枚举的方法,但很不幸,这道题不

适合,因为题目的附加条件中明确指出,s1、s2 字符串的最大长度为 100,暴力枚举的

时间开销难以承受。

换个角度,既然 0 <= s1.length, s2.length <= 100,那我们不妨利用动态规划来解题。

第一步,确定状态,我们假设 f[i][j]为使用了 i 个 s1 的字符,和 j 个 s2 的字符来匹配 s3

的前 i+j 个字符。

那么最初的状态肯定是 f[0][0] = true

因为空串和空串是匹配的,示例 3 也给出来了。

第二步,确定动态转移方程,第一步已经确定了前面的 i + j - 1 个字符,接下来如果要确

定 f[i][j]的字符,无非是从 f[i-1][j]和 f[i][j-1]两种状态转移,如果能够转移,则需要这二

者其中之一为真。

如果是从 f[i-1][j]转移来的话,s1.charAt(i)肯定要与 s3.charAt(i+j)相等,如果是从 f[i]

[j-1]转移来的话,s2.charAt(j)肯定要与 s3.charAt(i+j)相等。

为什么?

因为我们的 f[i][j]是用 i 个 s1 的字符和 j 个 s2 的字符来匹配 s3 的前 i+j 个字符,那么我们

的 f[i][j]的最后一个字符肯定是 s1.charAt(i)或者 s2.charAt(j),所以我们的动态转移方程

就出来了:

$

$

考虑到字符串存储的实际情况,我们的题解需要稍作改动。

int pos = i + j - 1;

if (i > 0) {

f[i][j] = f[i][j] || (f[i - 1][j] && (s1.charAt(i - 1) == s3.charAt(pos)));

}

if (j > 0) {

f[i][j] = f[i][j] || (f[i][j - 1] && (s2.charAt(j - 1) == s3.charAt(pos)));

}

注意,字符串是以 0 位置开始计数的。

答案一目了然:因为题目要求的是整个字符串的匹配,所以 s1 和 s2 都必须全都用完,那

么答案自然存储在 f[s1.length()][s2.length()]。

class Solution {

// 定义 isInterleave 方法

public boolean isInterleave(String s1, String s2, String s3) {

// 获取 s1 和 s2 的长度

int n = s1.length(), m = s2.length();

// 如果 s1 和 s2 长度之和不等于 s3 的长度,直接返回 false

if (n + m != s3.length()) {

return false;

}

// 创建一个二维数组 f 来保存中间结果,f[i][j]表示 s1 的前 i 个字符和 s2 的前 j 个字符是否

能交错组成 s3 的前 i+j 个字符

boolean[][] f = new boolean[n + 1][m + 1];

// 初始化 f[0][0]为 true,表示空字符串是可以交错组成的

f[0][0] = true;

// 遍历 s1 和 s2 的所有字符组合

for (int i = 0; i <= n; i++) {

for (int j = 0; j <= m; j++) {

// pos 用于定位当前在 s3 中的位置

int pos = i + j - 1;

// 如果 i 大于 0,我们需要检查 s1 的第 i 个字符和 s3 的第 pos 个字符是否匹配

if (i > 0) {

f[i][j] = f[i][j] || (f[i - 1][j] && s1.charAt(i - 1) == s3.charAt(pos));

}

// 如果 j 大于 0,我们需要检查 s2 的第 j 个字符和 s3 的第 pos 个字符是否匹配

if (j > 0) {

f[i][j] = f[i][j] || (f[i][j - 1] && s2.charAt(j - 1) == s3.charAt(pos));

}

}

}

// 最终返回 f[n][m],它表示 s1 和 s2 是否能交错组成 s3

return f[n][m];

}

}

假如输入是 s1 = “aabcc”, s2 = “dbbca”, 和 s3 = “aadbbcbcac”,我们来模拟一下整个题解的

过程:

1. 当 i 和 j 都为 0 时,f[0][0] 为 true。

2. 当 i = 1 时,我们检查 s1[0] 是否等于 s3[0],发现 'a' == 'a',所以 f[1][0] 为 true。

3. 当 i = 2 时,我们继续检查 s1[0...1] 是否可以形成 s3[0...1],发现 'aa' == 'aa',所

以 f[2][0] 为 true。

4. 对于 j = 1,我们检查 s2[0] 是否等于 s3[0],因为 'd' != 'a',f[0][1] 保持 false。

5. 对于 i = 1 和 j = 1,我们需要检查 s1[0] 和 s2[0] 是否可以交错形成 s3[0...1]。因为

f[1][0] 是 true 并且 'a' == 'a',同时 f[0][1] 是 false,所以 f[1][1] 变成 true。

继续,对于 i = 2, j = 1,s1[0...1] 和 s2[0] 是否可以交错形成 s3[0...2]。以此类推,我

们继续检查所有可能的 i 和 j 的组合。

最终,我们填充完整个 f 数组。如果 f[s1.length()][s2.length()] 为 true,则表示 s1 和 s2

可以交错形成 s3。

来看一下题解效率:

097.%E4%BA%A4%E9%94%99%E5%AD%97%E7%AC%A6%E4%B8%B2-

20231106170113.png

我们把题解改造为 ACM 输入输出模式,然后 debug 看一下:

import java.util.Scanner;

public class InterleavingString {

public static void main(String[] args) {

Scanner scanner = new Scanner(System.in);

// 读取输入

String s1 = scanner.nextLine();

String s2 = scanner.nextLine();

String s3 = scanner.nextLine();

// 调用函数并输出结果

System.out.println(isInterleave(s1, s2, s3));

}

public static boolean isInterleave(String s1, String s2, String s3) {

int n = s1.length(), m = s2.length();

if (n + m != s3.length()) {

return false;

}

boolean[][] f = new boolean[n + 1][m + 1];

f[0][0] = true;

for (int i = 0; i <= n; i++) {

for (int j = 0; j <= m; j++) {

int p = i + j - 1;

if (i > 0) {

f[i][j] |= (f[i - 1][j] && s1.charAt(i - 1) == s3.charAt(p));

}

if (j > 0) {

f[i][j] |= (f[i][j - 1] && s2.charAt(j - 1) == s3.charAt(p));

}

}

}

return f[n][m];

}

}

可以 debug 看一下输入输出

097.%E4%BA%A4%E9%94%99%E5%AD%97%E7%AC%A6%E4%B8%B2-

20231106171013.png

总结

一不小心,我们又解决了一道动态规划的难题!随着练习,我们可以发现,动态规划并非

什么恐怖的怪兽,它有着独特的解题过程和方法,是可以被攻克的,只要我们不断练习,

最终都会将其拿下。

力扣链接:https://leetcode.cn/problems/interleaving-string/

开通会员 本次下载免费

所有资料全部免费下载! 推荐用户付费下载获取返佣积分! 积分可以兑换商品!
一键复制 下载文档 联系客服