Leetcode 97:交错字符串

该博客介绍了LeetCode第97题的交错字符串问题,通过动态规划的方法来解决。作者详细解释了暴力动态规划、带备忘录的动态规划以及二维数组动态规划三种解法,并分析了它们的时间复杂度。最后,给出了每种解法的代码实现。

问题

Given s1, s2, s3, find whether s3 is formed by the interleaving of s1 and s2.
Example 1:
Input: s1 = “aabcc”, s2 = “dbbca”, s3 = “aadbbcbcac”
Output: true
Example 2:
Input: s1 = “aabcc”, s2 = “dbbca”, s3 = “aadbbbaccc”
Output: false

分析

动态规划解的字符串问题
很容易想到
暴力动态规划:
以Example1为例,s1的第一个字符与s3的第一个字符匹配;如果满足s3.substring(1)是s1.substring(1)与s2的交错字符串,则s3是s1和s2的交错字符串。
这里注意:优先匹配s1的字符还是优先匹配s2的字符可能会导致最终匹配结果的成功或失败;所以匹配完s1的首个字符之后(若此次匹配失败),应该匹配s2的首个字符与s3的首个字符。
举个栗子:
s1=“c”,s2=“ca”,s3=“cac”
如果先匹配s1的第一个字符,最终匹配结果为false;
这是匹配s2的第一个字符与s3的第一个字符,最终匹配结果为true。
时间复杂度:O(2^(m+n)),不出意外的超时了

带备忘录的动态规划:(来自官方解答)
使用二维备忘录矩阵mem[i][j],记录s1.substring(i),s2.substring(j)是否是s3.substring(k)的交错字符串。(这里,满足s1.length()-i+s2.length()-j=s3.length()-k)
备忘录初始化为-1;memo[i][j]=1是,表示剩余字符匹配成功;memo[i][j]=0时,表示匹配失败。
优点在于:减少重复递归。
时间复杂度:O(2^(m+n)),意料之外的也超时了,毕竟还是这个级别的复杂度。

一个由于太困和纯粹不想学习并只想听歌的下午啊 把剩下两个方法糊弄一下

二维数组的动态规划:(来自官方解答,too)
本题存在这样一个规则:
对于字符串s1,s2和s3,分别记三者当前的指针序号为i,j,k。(位置序号从0开始)
设一个二维数组dp[i][j],表示s1的0—i位置的子串和s2的0—j位置的子串与s3的0—i+j+1位置的子串是否可以交错生成,如果为true,表示可以;vice visa。
规则是:

  • 如果s3的i+j+1位置的字符既不与s1的i位置的字符相同,又不与s2的j位置的字符相同,则上面那句话不成立(即不可有子串交错生成),dp[i][j]=false
  • 如果s1的i位置与s3的i+j+1位置字符相同,则为了使s1的0—i位置和s2的0—j位置能交错生成字符串s3的0—i+j+1位置,则要求s1的0—i-1位置和s2的0—j位置能交错生成s3的0—i+j位置,即dp[i-1][j]=true;当前以上两条件都满足时,dp[i][j]=true;vice visa.
  • 如果s2的j位置与s3的i+j+1位置字符相同,则要判断dp[i][j-1]

(其中第二个和第三条件只要满足一个,则该位置的取值都为true)
(啊我可能没说清楚……有点不清楚但又感觉是这样没错的=3=)

时间复杂度:O(m*n),只需要动态更新一个二维数组的值即可。

代码

暴力动规:

public boolean isInterleave(String s1, String s2, String s3) {
	if (s1 == null || s2 == null || s2 == null)
		return false;
	if ("".equals(s3) && "".equals(s2) && "".equals(s1))
		return true;
	if ("".equals(s3) && ((!"".equals(s1)) || (!"".equals(s2))))
		return false;
	return getResult(s1, s2, s3);
}

private boolean getResult(String s1, String s2, String s3) {
	// TODO Auto-generated method stub
	if (s3.length() != s1.length() + s2.length())
		return false;
	if ("".equals(s3) && "".equals(s2) && "".equals(s1))
		return true;
	if ("".equals(s3) && ((!"".equals(s1)) || (!"".equals(s2))))
		return false;
	char ch = s3.charAt(0);
	boolean ans = false;
	if (s1.length() >= 1 && s1.charAt(0) == ch) {
		ans = getResult(s1.substring(1), s2, s3.substring(1));
	}
	if (ans)
		return ans;

	if (s2.length() >= 1 && s2.charAt(0) == ch) {
		ans = getResult(s1, s2.substring(1), s3.substring(1));
	}
	return ans;
}

带备忘录的动规:

public boolean isInterleave2(String s1, String s2, String s3) {
	int[][] memo = new int[s1.length()][s2.length()];
	for (int i = 0; i < s1.length(); i++) {
		for (int j = 0; j < s2.length(); j++) {
			memo[i][j] = -1;
		}
	}
	return getResult2(s1, 0, s2, 0, s3, 0, memo);
}

private boolean getResult2(String s1, int i, String s2, int j, String s3, int k, int[][] memo) {
	// TODO Auto-generated method stub
	if (i == s1.length()) {
		return s3.substring(k).equals(s2.substring(j));
	}
	if (j == s2.length()) {
		return s3.substring(k).equals(s1.substring(i));
	}
	if (s1.charAt(i) == s3.charAt(k)) {
		memo[i][j] = getResult2(s1, i + 1, s2, j, s3, k + 1, memo) ? 1 : 0;
	}
	if (memo[i][j] == 1)
		return true;
	if (s2.charAt(j) == s3.charAt(k)) {
		memo[i][j] = getResult2(s1, i, s2, j + 1, s3, k + 1, memo) ? 1 : 0;
	}
	return memo[i][j] == 1 ? true : false;
}

二维数组的动态规划:

public boolean isInterleave3(String s1, String s2, String s3) {
	// 方法三 来自Leetcode官方解答(一个有点智障的下午)
	// 我几时不智障
	if (s1.length() + s2.length() != s3.length())
		return false;
	boolean[][] dp = new boolean[s1.length() + 1][s2.length() + 1];
	for (int i = 0; i < dp.length; i++) {
		for (int j = 0; j < dp[0].length; j++) {
			if (i == 0 && j == 0) {
				dp[i][j] = true;
			} else if (i == 0) {
				dp[i][j] = s2.substring(0, j).equals(s3.substring(0, j));
			} else if (j == 0) {
				dp[i][j] = s1.substring(0, i).equals(s3.substring(0, i));
			}
			// 比较当前字符与s3的当前位置的字符是否匹配
			else {
				dp[i][j] = (s3.charAt(i + j - 1) == s1.charAt(i - 1) && dp[i - 1][j])
						|| (s3.charAt(i + j - 1) == s2.charAt(j - 1) && dp[i][j - 1]);
			}
		}
	}
	return dp[s1.length()][s2.length()];
}
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值