美文网首页
Java解决LCS(Longest Common Sequenc

Java解决LCS(Longest Common Sequenc

作者: 夹尾妖 | 来源:发表于2019-02-17 15:55 被阅读0次

    问题描述

    LCS问题可以分作最长公共子序列问题和最长公共子集问题,其区别在于前者要求的是连续的属于两个字符串序列的子集,而后者则不要求连续。如:
    字符串一:桃李漫山过眼空。也宜恼损杜陵翁。若将玉骨冰姿比,李蔡为人在下中。
    字符串二:过眼滔滔云共雾,算人间知己吾和汝。人有病,天知否?
    一时书袋一时爽,一直吊一直爽!

    公共子集

    image.png

    公共子序列

    image.png

    问题解析

    公共子集

    现有两个字符串str_1,str_2,str_1.subString[0,m+1]和str.subString[0,n+1]代表这两个字符串的前m项和前n项。LCS_1(String,String)方法返回两个字符串的最长公共子集。传参中两个字符串可以分立到两个互补的场景中考虑即两个字符串的尾项是否相等。
    尾项相等
    由公共子集的特性可知,相同的尾项一定会给最终的LCS结果作出+1的贡献,所以在这种情况下数学表达式为:
    LCS_1(str_1.subString(0,m+1),str.subString(0,n+1))= LCS_1(str_1.subString(0,m),str.subString(0,n))+1
    尾项不相等
    尾项不相等时我们分别考虑两个字符串除去尾项的情况。所以表达式为:
    LCS_1(str_1.subString(0,m+1),str.subString(0,n+1))= max(LCS_1(str_1.subString(0,m+1),str.subString(0,n)),LCS_1(str_1.subString(0,m),str.subString(0,n+1)))
    最后加入初始条件和边界条件即构成了此题的动态规划算法。

    公共子序列

    本题需要构建两个字符串在每个位置上的重合矩阵。如"12345"和"34567":
    00100
    00010
    00001
    00000
    00000
    此时矩阵只保存了位置重合与否信息,我们对其进行优化,当str_2[m]str_2[n]为相同字符时,arr[m][n]的值为arr[m-1][n-1]+1。此时矩阵变为:
    00100
    00020
    00003
    00000
    00000
    现在矩阵中已经包含有公共子串的长度信息,于是我们维护一个整型保存当前循环中的LCS长度并维护一个Stack<Integer>(为了兼容同时存在多个LCS解的情况)以获得该子串尾项在矩阵中的坐标。到了这一步我们牺牲了O(nm)的空间复杂度换得了O(nm)的时间复杂度。但是仍有方法进一步减少内存空间的损耗。上述前提中我们已经通过开辟整型和栈在当前循环中保存了最长长度以及结果坐标,因此我们无需再维护一个完整而冗杂的矩阵,而只需要维护矩阵中当前循环中使用的每一行(即单行)即可。唯一需要注意的问题是算法要求保存前一行中arr[m-1][n-1]的状态,因此我们通过逆序刷新的方式,如此一来在循环到arr[m]时,arr[m-1]保存的正是上一个循环中的字符重合状态。

    不严谨代码

    import java.util.Scanner;
    import java.util.Stack;
    /**
     * 任意输入两个字符串,求出这两个字符串的公共子序列
     * */
    public class LCS1 {
        public static void main(String[] args) {
            Scanner sc = new Scanner(System.in);
            String x = sc.next();
            String y = sc.next();
            getLCS(x,y);
            sc.close();
        }
        
        public static void getLCS(String x, String y){
            
            char[] s1 = x.toCharArray();
            char[] s2 = y.toCharArray();
            int her = x.length();
            int ver = y.length();
            int[][] array = new int[her][ver];
            
            for(int m = 0; m < her; m++){//filling the LCS matrix using dynamic planning
                for(int n = 0; n < ver; n++){
                    if(s1[m] == s2[n]){
                        if(m==0 || n==0) array[m][n] = 1;//default case
                        else array[m][n] = array[m-1][n-1] + 1;
                    }else{
                        if(m==0 || n==0) array[m][n] = 0;//default case
                        else array[m][n] = max(array[m -1][n], array[m][n -1]);
                    }
                }
            }
    //      for(int m = 0; m < her; m++){//output the auxiliary matrix
    //          for(int n = 0; n < ver; n++){
    //              System.out.print(array[m][n]);
    //          }
    //          System.out.println();
    //      }
            Stack<Character> stack = new Stack<>();
            int i = x.length() - 1;
            int j = y.length() - 1;
            
            while((i >= 0) && (j >= 0)){//push result into stack
                if(s1[i] == s2[j]){
                    stack.push(s1[i]);
                    i--;
                    j--;
                }else{
                    if(i==0) j--;
                    else if(j==0) i--;
                    else if(array[i][j-1] > array[i-1][j]){
                        j--;
                    }
                    else{
                        i--;
                    }
                }
            }
            
            while(!stack.isEmpty()){
                System.out.print(stack.pop());
            }           
        }
        
        public static int max(int a, int b){
            return (a > b)? a : b;
        }
    }
    
    import java.util.ArrayList;
    import java.util.List;
    import java.util.Scanner;
    import java.util.Stack;
    
    public class LCS2 {
        public static List<String> findLongestCommonSet(String str1,String str2){
            char[] chArr1 = str1.toCharArray();
            int len1 = chArr1.length;
            char[] chArr2 = str2.toCharArray();
            int len2 = chArr2.length;
            Stack<Integer> indexes = new Stack<>();
            List<String> res = new ArrayList<String>();
            int maxLen = 0;
            int[] lenArr = new int[len1>len2?len1:len2];
            for(int i=0;i<len1;i++) {
                for(int j=len2-1;j>=0;j--) {//fill and flush the single array by reversed order
                    if(chArr1[i]==chArr2[j]) {
                        if(i==0 || j==0) lenArr[j] = 1;//default case
                        else lenArr[j] = lenArr[j-1]+1;
                    }
                    else lenArr[j]=0;
                    
                    /*record every largest length
                     * with corresponding indexes of last char
                      */
                    if(lenArr[j]>maxLen) {
                        indexes.clear();
                        maxLen = lenArr[j];
                        indexes.push(j);
                        
                    }
                    else if(lenArr[j]==maxLen) indexes.push(j);
                }
            }
            while(!indexes.isEmpty()) {
                int term = indexes.pop();
                res.add(str2.substring(term-maxLen+1,term+1));
            }
            System.out.println("Longest Common Sequence:"+maxLen);
            return res;
        }
        public static void main(String[] args) {
            Scanner sc = new Scanner(System.in);
            String x = sc.next();
            String y = sc.next();
            List<String> list = findLongestCommonSet(x, y);
            for (int i = 0; i < list.size(); i++) {
                System.out.println("第" + (i + 1) + "个公共子串:" + list.get(i));
            }
    
            sc.close();
        }
    }
    

    相关文章

      网友评论

          本文标题:Java解决LCS(Longest Common Sequenc

          本文链接:https://www.haomeiwen.com/subject/zitieqtx.html