找出两个字符串的最长公共子串

题目:

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

输入描述:

输入包括两行,第一行代表字符串srr1,第二行代表字符串str2。 1 ≤ length( str1), length(str2) ≤ 5000

输出描述:

输出包括一行,代表最长公共子串。

示例

输入
1AB2345CD 12345EF

输出
2345

解法一 : 二维数组划“捺”法

只需要几个变量,从右上角点开始,划“捺”
定义出发点,出发点一开始时(0,m-1)
出发点向左移动,一直移动到左上角以后,开始从左上角往左下角向下移动
在这里插入图片描述
代码如下

import java.io.*;
 
public class Main{
    public static void main(String[] args)throws IOException{
        BufferedReader read = new BufferedReader(new InputStreamReader(System.in));
        String s1 = read.readLine();
        String s2 = read.readLine();
        int n = s2.length();
        int m = s1.length();
        int col = m-1;//出发点的列
        int row = 0;//出发点的行
        int max=0;//最大长度
        int pos = -1;//因为我们的最长公共子串是从s2中截取的,所以这里用pos来标识s2中公共子串的结束位置索引
        while(col!=0 || row < n){
        //初始化 i,j
            int i = row;
            int j = col;
            int len = 0;
            while(i<n && j<m){//划“捺”,这里的while()里面的限制就是为了不让划“捺”划到超出整个棋盘
                if(s1.charAt(j) == s2.charAt(i)){
                    len++;
                    if(len>max){
                        max = len;
                        pos = i;
                    }
                    
                }else{
                    len=0;
                }
                i++;
                j++;
            }
            if(col == 0){//如果出发点已经移到了最左边了,那就只能向下方移动了
                row++;
            }else{ //如果没有移到最左边,那就继续往左边移动
                col--;
            }
        }
        String str = s2.substring(pos-max+1,pos+1);
        if(str.length()==0){
            str = "-1";
        }
        System.out.println(str);
    }
}

总结: 以后凡是看到这种求两个字符串公共部分的时候,首先想到在二维数组棋盘上划“捺”的办法。

解法二:动态规划法

dp[i][j]表示nums1[i...end]nums2[j...end]的最长公共前缀部分的长度
如果 nums1[i] == nums2[j],那么 dp[i][j] = dp[i + 1][j + 1] + 1
如果 nums1[i] != nums2[j],那么前缀部分肯定是不相同的,所以 dp[i][j] = 0
在这个过程中记录下dp数组的最大值即可,即 res
因为dp[i][j]是从dp[i + 1][j + 1]转移而来的,所以要倒着遍历数组

代码如下:

  public String findCommonSubStr(String str1, String str2) {
        int str1lenth = str1.length(), str2lenth = str2.length();
        int[][] dp = new int[str1lenth + 1][str2lenth + 1];
        int maxLength = 0; //公共子串的最大长度
        int pos = -1;//因为我们的最长公共子串是从s2中截取的,所以这里用pos来标识s2中公共子串的结束位置索引
        for (int i = str1lenth - 1; i >= 0; i--) {
            for (int j = str2lenth - 1; j >= 0; j--) {
                dp[i][j] = str1.charAt(i) == str2.charAt(j) ? dp[i + 1][j + 1] + 1 : 0;
                if (dp[i][j] > maxLength) {
                    maxLength = dp[i][j];
                    pos = j;
                }
            }
        }
        return str2.substring(pos, pos + maxLength);
    }

解法三: 滑动窗口法

先看个动画如下
img
假设动画中的 “12321”是字符串A, "32147"是字符串B, 如果我们把A比作一个轨道,B比作一辆火车
那么我们从动画中可以看到三个过程 ①B的火车头进入A轨道,在轨道里面一直往右边移动 但火车头没有离开A轨道 ②B火车头跑出出了A轨道,火车尾还没有进入A轨道 ③B火车尾进入A轨道,继续往前开,但是火车尾还没有离开A轨道

public String findCommonSubStr(String A, String B) {
        //一定要先明确A和B哪个长哪个短
        return A.length() < B.length() ? findMax(A, B) : findMax(B, A);
    }

    //A串的长度较小, B串的长度较大
    String findMax(String A, String B) {
        String result = "";
        String maxStr = "";
       //B的火车头进入A轨道,在轨道里面一直往右边移动 但火车头没有离开A轨道
        for (int len = 1; len <= A.length(); len++) {
            maxStr = maxStr(A, 0, B, B.length() - len, len);
            if (maxStr.length() > result.length()) {
                result = maxStr;
            }
        }
        // B火车头跑出出了A轨道,火车尾还没有进入A轨道
        for (int j = B.length() - A.length(); j >= 0; j--) {
            maxStr = maxStr(A, 0, B, j, A.length());
            if (maxStr.length() > result.length()) {
                result = maxStr;
            }
        }
        //B火车尾进入A轨道,继续往前开,但是火车尾还没有离开A轨道
        for (int i = 1; i < A.length(); i++) {
            maxStr = maxStr(A, i, B, 0, A.length() - i);
            if (maxStr.length() > result.length()) {
                result = maxStr;
            }
        }
        return result;
    }

    //A子串从i位置和Bzi子串的j位置 对齐,然后开始从头一一比对各个字符是否相同
    // len参数是指比对的最大长度
    String maxStr(String A, int i, String B, int j, int len) {
        int count = 0, max = 0;
        int pos = 0;
        for (int k = 0; k < len; k++) {
            if (A.charAt(i + k) == B.charAt(j + k)) {
                count++;
            } else if (count > 0) {
                if (count > max) {
                    max = count;
                    pos = j + k;
                }
                count = 0;
            }
        }
        //在上面的for循环中,当k=len-1时,count可能也++了,所以下面的代码还要做个特殊判断
        if (count > 0 && count > max) {
            max = count;
            pos = j + len;
        }
        return B.substring(pos - max, pos);
    }
  • 6
    点赞
  • 36
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

Java全栈研发大联盟

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

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

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

打赏作者

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

抵扣说明:

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

余额充值