Java解决LCS问题

9 篇文章 0 订阅
package dynamicprogramming;


/**
 * @author Eightn0
 * @create 2021-03-30 10:48
 * 分析:
 * LCS问题有两种,求两个字符串的最长公共序列、最长相同子串
 * 若字符串A为"helloworld",字符串B为"lol",则AB的最长公共子序列为"lol",最长公共子串为"lo"。
 * 对于字符串A和字符串B来说,设A的长度为lena,B的长度为lenb,A的第i个元素为i,B的第j个元素为j
 * 状态res[i,j]表示截至A的第i个元素和B的第j个元素的LCS结果
 * 首先分析最长公共序列问题:
 *      若AB的长度任一为0,则无相同子序列,题意要求输出-1,即res[i,j]=-1;
 *      若AB的长度均不为0,若A的第i个元素和B的第j个元素相等,那么最长公共子序列的长度+1,即res[i,j]=res[i-1,j-1]+1
 *                      若A的第i个元素和B的第j个元素不相等,那么最长公共子序列则取决于上一个状态中的最大值,即res[i,j]=max(res[i,j-1],res[i-1,j])
 * 然后分析最长相同子串问题:
 *      与最长公共序列不同的是,当寻找AB相同字符时中间出现断裂的时候,就停止本轮搜索了,题意保证str1和str2的最长公共子串存在且唯一,所以状态转移方程为
 *      res[i,j]=res[i-1,j-1]+1
 * Java编程实现:
 * 对于最长公共序列问题,首先用二维数组描述状态和状态转移方程,用stringBuffer记录相同元素,倒序输出元素(这里也可以用栈)。
 * 对于最长公共子串问题,仍然用二维数组描述状态和状态转移方程,但只要有长度和结束指针,内容可以用substring直接输出,无须另造stringbuffer
 */
public class LCS {

    /*给定两个字符串str1和str2,输出连个字符串的最长公共子序列。如过最长公共子序列为空,则输出-1。*/
    public String LCS1 (String s1, String s2) {

        StringBuffer stringBuffer = new StringBuffer();
        int lenA = s1.length();
        int lenB = s2.length();
        int[][] res = new int[lenA + 1][lenB + 1];

        for (int i = 1; i < lenA + 1; i++) {
            for (int j = 1; j < lenB + 1; j++) {
                if (s1.charAt(i - 1) == s2.charAt(j - 1)){
                        res[i][j] = res[i - 1][j - 1] + 1;
                    }else {
                        res[i][j] = Math.max(res[i - 1][j], res[i][j - 1]);
                    }
            }
        }

        while (lenA > 0 && lenB > 0) {
            if (s1.charAt(lenA - 1) == s2.charAt(lenB - 1)){
                    stringBuffer.append(s1.charAt(lenA - 1));
                    lenA --;
                    lenB --;
            } else {
                if (res[lenA][lenB - 1] > res[lenA - 1][lenB]) {//找大的那个方向,此处是左边大于上面,则该处的结果是来自左边
                    lenB --;
                } else if (res[lenA][lenB - 1] < res[lenA - 1][lenB]) {
                    lenA --;
                } else if (res[lenA][lenB - 1] == res[lenA - 1][lenB]) {
                    lenB --;
                }
            }
        }

        if (stringBuffer.length() == 0) return "-1";
        return stringBuffer.reverse().toString();
    }

    /*给定两个字符串str1和str2,输出两个字符串的最长公共子串,题目保证str1和str2的最长公共子串存在且唯一。*/
    public String LCS2 (String str1, String str2) {

        int max = 0;
        int end = 0;
        int lenA = str1.length();
        int lenB = str2.length();
        int[][] res = new int[lenA + 1][lenB + 1];

        for (int i = 1; i < lenA + 1; i++) {
            for (int j = 1; j < lenB + 1; j++) {
                if (str1.charAt(i - 1) == str2.charAt(j - 1)){
                    res[i][j] = res[i - 1][j - 1] + 1;
                }else {
                    res[i][j] = 0;
                }
                if (res[i][j] >= max){
                    max = res[i][j];
                    end=j-1;
                }
            }
        }
        if(max == 0){
            return "-1";
        }else{
            return str2.substring(end - max + 1, end + 1);
        }
    }


    public static void main(String[] args) {
        String str1 = "helloworld";
        String str2 = "lol";
        LCS lcs = new LCS();
        System.out.println(lcs.LCS2(str1, str2));
        //System.out.println(lcs.LCS1(str1, str2));
    }

}
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值