牛客经典 动态规划算法应用

题目描述

给定两个字符串str1和str2,输出两个字符串的最长公共子串,如果最长公共子串为空,输出-1。

输入  "1AB2345CD","12345EF"

返回值  "2345"

参考

链接:https://www.nowcoder.com/questionTerminal/f33f5adc55f444baa0e0ca87ad8a6aac?answerType=1&f=discussion
来源:牛客网

1.滑动窗口

  1. str1中按照窗口截取窗口子串,检查str2中是否包含,如果包含就扩展窗口
  2. 如果str2中不包含窗口子串,我们就移动窗口起始位置
  3. sb是用来记录最大的窗口子串的,窗口最小为0个单位

这个算法精妙的地方在于窗口大小不会缩小的,我们只需要找最大公共子串,所以一旦有某个窗口子串命中了,往后的窗口子串就不能小于上次命中的长度,所以我们在算法里可以看到当start++时必然伴随着end++,保证窗口不会变小  

public class Solution {
        // write code here
    /**
     * 滑动窗口算法
     *
     * @param str1 string字符串 the string
     * @param str2 string字符串 the string
     * @return string字符串
     */
    public static String LCS1(String str1, String str2) {
        // write code here
        StringBuilder sb = new StringBuilder();
        int start = 0, end = 1;
        while (end < str1.length() + 1) {
            // 判断截取窗口,如果包含窗口扩大end++
            // 如果不包含,start++ 缩小窗口
            if (str2.contains(str1.substring(start, end))) {
                if (sb.length() < end - start) {
                    sb.delete(0, sb.length());
                    sb.append(str1, start, end);
                }
            } else {
                //当start==end时,substring获取的是"",此时contains必然为true
                //所以当start == end时,必然会走end++分支
                //不会出现end>start的情况crash程序;
                start++;
            }
            end++;
        }
        if (sb.length() == 0) {
            return "-1";
        }
        return sb.toString();
    }
}

 

2.动态规划

    public static String LCS2(String str1, String str2) {
        if (str1 == null || str2 == null) return "-1";
        int n1 = str1.length(), n2 = str2.length();
        if (n1 == 0 || n2 == 0) return "-1";
        int[][] dp = new int[n1 + 1][n2 + 1];

        int maxLen = 0, x = 0;
        for (int i = 1; i <= n1; i++) {
            char ch1 = str1.charAt(i - 1);
            for (int j = 1; j <= n2; j++) {
                char ch2 = str2.charAt(j - 1);
                if (ch1 == ch2) {
                    dp[i][j] = dp[i - 1][j - 1] + 1;
                    if (dp[i][j] > maxLen) {
                        maxLen = dp[i][j];
                        x = i;
                    }
                }
            }
        }

        return maxLen == 0 ? "-1" : str1.substring(x - maxLen, x);
    }

这里dp的索引是从1开始,0位是防止数组越界,并且也有实际意义,可以理解为空子串,结果自然也是0

相比之下优点

  1. 最大公共子串的出现,必然发生在有字符相等的时候。这个应该很好理解,所以算法里只关心出现这个情况的dp值,但是同时需要注意,此时dp里记录的值不再是连续的值了,不能再认为dp[i][j]描述的是str1中从0~istr2中从0~j的最大公共子串,此时的dp记录值有点类似滑动窗口了,某一段时间内是持续增长的,所以他用maxLen来记录出现的最大值
  2. 几个临时变量存储。首先是maxLenx,这两个变量描述了一个子串,当出现更大的字串时更新这两个值;还有ch1ch2,也有一定性能优化
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值