动态规划-交错字符串

593 阅读2分钟

这是我参与更文挑战的第14天,活动详情查看: 更文挑战

如果你对动态规划不熟悉,望转到该篇 \color{red}{如果你对动态规划不熟悉,望转到该篇~}

肝了好多天-动态规划十连-超细腻解析|刷题打卡

这道题很有意思,不看后悔!😄😄😄 \color{green}{这道题很有意思,不看后悔!😄 😄 😄 ~}

什么题可以选择动态规划来做?

1.计数

  • 有多少种方式走到右下角
  • 有多少种方法选出k个数是的和是sum

2.求最大值最小值

  • 从左上角走到右下角路径的最大数字和
  • 最长上升子序列长度

3.求存在性

  • 取石子游戏,先手是否必胜
  • 能不能选出k个数使得和是sum

leecode 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 连接。

image.png

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

输出:true

示例 2:

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

输出:false

示例 3:

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

输出:true


--

动态规划四步走~~~ ❤️❤️❤️❤️

2.1. 动态规划组成部分1:确定状态

简单的说,解动态规划的时候需要开一个数组,数组的每个元素f[i]或者f[i][j]代表什么,类似数学题中x, y, z代表什么

最后一步

类似这类题,我们可以画一个二维图,来看一下 例如: s1 = "aab" s2 = "bac" s3= "abacab"

i/j0aab
011
b1
a1
c111

可以发现,通过二维图我们就找到了一条路径,这条路径恰恰就是s1和s2交错形成的。

很明显,最后一步就是dp[i][j],坐标点就是(3,3)

子问题

这道题是否跟不同路径很类似呢?

我们如果想要形成这条路径,可以根据i-1和j-1来看,如果说si的i-1的坐标正好在s3这条路径上

同理,s2的j-1的坐标正好在s3这条路径上

同时,还必须的满足之前的点也在这条路径上才行。

是不是觉得我很帅 \color{yellow}{是不是觉得我很帅 ~}❤️❤️❤️

2.2. 动态规划组成部分2:转移方程

看了子问题分析,其实就是对左边和右边这两种情况的分析。

转移方程就很好写了:

dp[i,j] = (dp[i-1][j] &&s3[i+j-1] == s1[i-1]) || (dp[i][j-1] && s3[i+j-1] == s2[j-1])

2.3. 动态规划组成部分3:初始条件和边界情况

dp[0][0] = true

如果i= 0,看j的左边是否在路径上,如果j= 0,看i的左边是否在路径上。或者直接i>0,j>0判断就行了。

2.4. 动态规划组成部分4:计算顺序

s1的每个字符遍历s2的每个字符

参考代码

GO语言版

 func isInterleave(s1 string, s2 string, s3 string) bool {
        n, m, t := len(s1), len(s2), len(s3)
        if (n + m) != t {
            return false
        }
         // 动态定义一个二维数组
        f := make([][]bool, n + 1)
        for i := 0; i <= n; i++ {
            f[i] = make([]bool, m + 1)
        }
        f[0][0] = true
        for i := 0; i <= n; i++ {
            for j := 0; j <= m; j++ {
                p := i + j - 1
                if i > 0 { // 之前的点在路径上 && 左边点或者上边点 也在这条路径上。
                    f[i][j] = f[i][j] || (f[i-1][j] && s1[i-1] == s3[p])
                }
                if j > 0 {
                    f[i][j] = f[i][j] || (f[i][j-1] && s2[j-1] == s3[p])
                }
            }
        }
        return f[n][m]
    }



JAVA版

 
    public boolean isInterleave(String s1, String s2, String s3) {
            int m = s1.length(), n = s2.length();
            if (s3.length() != m + n) return false;
            // 动态规划,dp[i,j]表示s1前i字符能与s2前j字符组成s3前i+j个字符;
            boolean[][] dp = new boolean[m+1][n+1];
            dp[0][0] = true;
            for (int i = 1; i <= m && s1.charAt(i-1) == s3.charAt(i-1); i++) {
                dp[i][0] = true; 
            }
            for (int j = 1; j <= n && s2.charAt(j-1) == s3.charAt(j-1); j++) {
                dp[0][j] = true; 
            }
            for (int i = 1; i <= m; i++) {
                for (int j = 1; j <= n; j++) {
                    dp[i][j] = (dp[i - 1][j] && s3.charAt(i + j - 1) == s1.charAt(i - 1))
                            || (dp[i][j - 1] && s3.charAt(i + j - 1) == s2.charAt(j - 1));
                }
            }
            return dp[m][n];
        }


    

❤️❤️❤️❤️

非常感谢人才们能看到这里,如果这个文章写得还不错,觉得有点东西的话 求点赞👍 求关注❤️ 求分享👥 对帅气欧巴的我来说真的 非常有用!!!

如果本篇博客有任何错误,请批评指教,不胜感激 !

文末福利,最近整理一份面试资料《Java面试通关手册》,覆盖了Java核心技术、JVM、Java并发、SSM、微服务、数据库、数据结构等等。获取方式:GitHub github.com/Tingyu-Note…,更多内容关注公号:汀雨笔记,陆续奉上。