97. Interleaving String

题目描述(困难难度)

在这里插入图片描述
在两个字符串 s1 和 s2 中依次取字母,问是否可以组成 S3。什么意思呢?比如 s1 = abc , s2 = de,s3 = abdce。

s1 取 1 个 字母得到 a,s1 再取个字母得到 ab,s2 取个字母得到 abd, s1 取 1 个 字母得到 abdc, s2 取 1 个 字母得到 abdce,然后就得到了 s3,所以返回 true。

解法一 回溯法

如果我们简化下问题,如果 s1 和 s2 中不含有重复的字母,比如 s1 = abc,s2 = de,s3 = abdce。

这样是不是就简单多了。我们只需要三个指针,依次遍历字符串。

i 和 k 的指的字母相等,所以 i 后移,k 后移
a b c
^
i

d e
^
j

a b d c e
^
k

i 和 k 的指的字母相等,所以 i 后移,k 后移
a b c
  ^
  i

d e
^
j

a b d c e
  ^
  k

j 和 k 的指的字母相等,所以 j 后移,k 后移
a b c
    ^
    i

d e
^
j

a b d c e
    ^
    k

就这样比较下去,如果 i,j,k 都成功移动到了末尾即成功。

但是这道题 s1 和 s2 中会有重复的字符出现,比如下边的情况

a d c
  ^
  i

d e
^
j

a d c e
  ^
  k

此时 i 和 j 指向的字母都和 k 相等,此时该怎么办呢?

回溯法!是的,我们先尝试 i 和 k 后移,然后看能不能成功。不行的话我们再回溯回来,把 j 和 k 后移。

public class Interleaving_String {
	
	public static boolean isInterleave(String s1,String s2,String s3) {
		return getAns(s1,0,s2,0,s3,0);
	}
	
	private static boolean getAns(String s1,int i,String s2,int j,String s3,int k) {
		
		//长度不匹配直接返回false
		if(s1.length()+s2.length()!=s3.length()) return false;
		
		if(i==s1.length() && j==s2.length() && k==s3.length()) return true;
		
		//i到达了末尾,直接移动j和k不停比较
		if(i==s1.length()) {
			while(j<s2.length()) {
				if(s2.charAt(j)!=s3.charAt(k)) {
					return false;
				}
				j++;
				k++;
			}
			return true;
		}
		
		//j到达了末尾,直接移动i和k不停比较
		if(j==s2.length()) {
			while(i<s1.length()) {
				if(s1.charAt(i)!=s3.charAt(k)) {
					return false;
				}
				i++;
				k++;
			}
			return true;
		}
		
		//判断i和k指向的字符串是否相等
		if(s1.charAt(i)==s3.charAt(k)) {
			if(getAns(s1,i+1,s2,j,s3,k+1)) {
				return true;
			}
		}
		
		//移动i和k失败,尝试移动j和k
		if(s2.charAt(j)==s3.charAt(k)) {
			if(getAns(s1,i,s2,j+1,s3,k+1)) {
				return true;
			}
		}
		return false;
	}
	
	public static void main(String args[]) {
		String s1="aabcc";
		String s2="dbbca";
		String s3="aadbbcbcac";
		boolean ans=isInterleave(s1,s2,s3);
		System.out.println(ans);
	}
}
解法二 动态规划

在现在这种方法中,我们用另一种思路来考虑同样的问题。这里我们考虑用 s 1 s1 s1 s 2 s2 s2的某个前缀是否能形成 s 3 s3 s3 的一个前缀。

这个方法的前提建立于:判断一个 s 3 s3 s3的前缀(用下标 k k k 表示),能否用 s 1 s1 s1 s 2 s2 s2的前缀(下标分别为 i i i j j j),仅仅依赖于 s 1 s1 s1 i i i 个字符和 s 2 s2 s2 j j j 个字符,而与后面的字符无关。

为了实现这个算法, 我们将使用一个 2D 的布尔数组 d p dp dp d p [ i ] [ j ] dp[i][j] dp[i][j]表示用 s 1 s1 s1的前 ( i + 1 ) (i+1) (i+1) s 2 s2 s2 的前 ( j + 1 ) (j+1) (j+1) 个字符,总共$ (i+j+2)$个字符,是否交错构成 s 3 s3 s3 的前缀。为了求出 d p [ i ] [ j ] dp[i][j] dp[i][j],我们需要考虑 2 种情况:

  • s 1 s1 s1的第 i i i 个字符和 s 2 s2 s2 的第 j j j 个字符都不能匹配 s 3 s3 s3的第 k k k 个字符,其中 k = i + j + 1 k=i+j+1 k=i+j+1 。这种情况下, s 1 s1 s1 s 2 s2 s2的前缀无法交错形成 s 3 s3 s3 长度为 k + 1 k+1 k+1的前缀。因此,我们让 d p [ i ] [ j ] dp[i][j] dp[i][j]为 False。

  • s 1 s1 s1的第 i i i个字符或者 s 2 s2 s2 的第 j j j个字符可以匹配 s 3 s3 s3的第 k k k字符,其中 k = i + j + 1 k=i+j+1 k=i+j+1 。假设匹配的字符是 x x x 且与 s 1 s1 s1的第 i i i个字符匹配,我们就需要把 x x x 放在已经形成的交错字符串的最后一个位置。此时,为了我们必须确保 s 1 s1 s1的前 ( i − 1 ) (i-1) (i1)个字符和 s 2 s2 s2的前 j j j个字符能形成 s 3 s3 s3的一个前缀。类似的,如果我们将 s 2 s2 s2的第 j j j个字符与 s 3 s3 s3的第 k k k个字符匹配,我们需要确保 s 1 s1 s1的前 i i i个字符和 s 2 s2 s2的前 ( j − 1 ) (j-1) (j1)个字符能形成 s 3 s3 s3的一个前缀,我们就让 d p [ i ] [ j ] dp[i][j] dp[i][j]为 True 。

最终结果如下图所示。
在这里插入图片描述
代码如下:

public class Interleaving_String2 {
	
	public static boolean isInterleave(String s1, String s2, String s3) {
		
		if(s3.length()!=s1.length()+s2.length()) return false;
		
		boolean[][]dp=new boolean[s1.length()+1][s2.length()+1];
		for(int i=0;i<=s1.length();i++) {
			for(int j=0;j<=s2.length();j++) {
				if(j==0 && j==0) {
					dp[i][j]=true;
				}else if(i==0) {
					dp[i][j]=dp[i][j-1] && s2.charAt(j-1)==s3.charAt(i+j-1);
				}else if(j==0) {
					dp[i][j]=dp[i-1][j] && s1.charAt(i-1)==s3.charAt(i+j-1);
				}else {
					dp[i][j]=(dp[i][j-1] && s2.charAt(j-1)==s3.charAt(i+j-1))||dp[i-1][j] && s1.charAt(i-1)==s3.charAt(i+j-1);
				}
			}
		}
		return dp[s1.length()][s2.length()];
       
    }
	public static void main(String args[]) {
		String s1="aabcc";
		String s2="dbbca";
		String s3="aadbbcbcac";
		boolean ans=isInterleave(s1,s2,s3);
		System.out.println(ans);
	}
}

在这里插入图片描述

参考文献

1.https://zhuanlan.zhihu.com/p/71855844
2.使用二维动态规划
3.https://www.youtube.com/watch?v=HmAF9xeS_2I

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

安替-AnTi

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值