题目:

给定三个字符串 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 连接。

剑指offer96:字符串交织_算法


示例一:输入:s1 = “aabcc”, s2 = “dbbca”, s3 = “aadbbcbcac”

输出:true

示例二:输入:s1 = “aabcc”, s2 = “dbbca”, s3 = “aadbbbaccc”

输出:false

示例三:输入:s1 = “”, s2 = “”, s3 = “”

输出:true

分析:

每步从字符串s1或s2中选出一个字符交织生产字符串s3中的一个字符,那么交织生成的字符串s3中的所有字符需要多少步骤,每一步即可能从字符串s1中选择一个字符也可能从字符串s2中选择一个字符,也就是说每步可能面临两个选择。该问题并没有要求列出所有字符串s1和s2交织得到字符串s3的方法,而只是判断能否将字符串s1和s2交织得到字符串s3,只是判断该问题的解是否存在,因而采用动态规划解决该问题。

确定状态转移方程:如果第一个字符串的最后一个字母和合成字符串最后的字母相同,则f(i,j)=f(i-1,j),如果第二个字符串的最后一个字母和合成字符串最后的字母相同,则f(i,j)=f(i,j-1)。-1代表空串

剑指offer96:字符串交织_算法_02

剑指offer96:字符串交织_字符串_03

代码:

public class IsInterleave {
public static void main(String[] args) {
String s1 = "aabcc";
String s2 = "dbbca";
String s3 = "aadbbcbcac";
IsInterleave isInterleave = new IsInterleave();
boolean interleave2 = isInterleave.isInterleave2(s1, s2, s3);
System.out.println(interleave2);
}
public boolean isInterleave1(String s1, String s2, String s3) {
if (s1.length() + s2.length() != s3.length()){
return false;
}
boolean[][] dp = new boolean[s1.length()+1][s3.length()+1];
dp[0][0] = true;
for (int i = 0; i < s1.length(); i++) {
dp[i+1][0] = s1.charAt(i) == s3.charAt(i)&&dp[i][0];
}
for (int i = 0; i < s2.length(); i++) {
dp[0][i+1] = s2.charAt(i) == s3.charAt(i)&&dp[0][i];
}
for (int i = 0; i < s1.length(); i++) {
for (int j = 0; j < s2.length(); j++) {
char ch1 = s1.charAt(i);
char ch2 = s2.charAt(j);
char ch3 = s3.charAt(i+j+1);
dp[i+1][j+1] = (ch1 == ch3 && dp[i][j+1]) || (ch2 == ch3 && dp[i+1][j]);
}
}
return dp[s1.length()][s2.length()];
}
// 优化空间效率
public boolean isInterleave2(String s1, String s2, String s3){
if (s1.length() + s2.length() != s3.length()){
return false;
}
boolean[][] dp = new boolean[2][s3.length()+1];
dp[0][0] = true;
for (int i = 0; i < s2.length(); i++) {
dp[0][i+1] = s2.charAt(i) == s3.charAt(i)&&dp[0][i];
}
for (int i = 0; i < s1.length(); i++) {
dp[(i+1)%2][0] = s1.charAt(i) == s3.charAt(i)&&dp[i%2][0];
for (int j = 0; j < s2.length(); j++) {
char ch1 = s1.charAt(i);
char ch2 = s2.charAt(j);
char ch3 = s3.charAt(i+j+1);
dp[(i+1)%2][j+1] = (ch1 == ch3 && dp[i%2][j+1]) || (ch2 == ch3 && dp[(i+1)%2][j]);
}
}
return dp[s1.length()%2][s2.length()];
}
// 接着优化空间效率
public boolean isInterleave3(String s1, String s2, String s3){
if (s1.length() + s2.length() != s3.length()){
return false;
}
if (s1.length() < s2.length()){
return isInterleave3(s2,s1,s3);
}
boolean[] dp = new boolean[s2.length() + 1];
dp[0] = true;
// 相当于二维数组第一行
for (int j = 0; j < s2.length(); j++) {
dp[j+1] = s2.charAt(j) == s3.charAt(j)&&dp[j];
}
for (int i = 0; i < s1.length(); i++) {
// 相当于逐个判断二维数组第一列是true还是false
dp[0] = dp[0] && s1.charAt(i) == s3.charAt(i);
for (int j = 0; j < s2.length(); j++) {
char ch1 = s1.charAt(i);
char ch2 = s2.charAt(j);
char ch3 = s3.charAt(i+j+1);
dp[j+1] = (ch1 == ch3 && dp[j+1]) || (ch2 == ch3 &&dp[j]);
}
}
return dp[s2.length()];
}
}

剑指offer96:字符串交织_动态规划_04