codewars—Longest Common Subsequence

来源:互联网 发布:熊片数据库新地址 编辑:程序博客网 时间:2024/05/23 14:25

题目地址:https://www.codewars.com/kata/longest-common-subsequence/train/java

题意:求两个字符串的最大公共序列

最长公共子串(Longest CommonSubstring)和最长公共子序列(LongestCommon Subsequence, LCS)的区别:子串(Substring)是串的一个连续的部分,子序列(Subsequence)则是从不改变序列的顺序,而从序列中去掉任意的元素而获得的新序列;更简略地说,前者(子串)的字符的位置必须连续,后者(子序列LCS)则不必。

    @Test    public void exampleTests() {        long startTime=System.nanoTime();   //获取开始时间          assertEquals("", Solution.lcs("a", "b"));        assertEquals("abc", Solution.lcs("abcdef", "abc"));        assertEquals("acf", Solution.lcs("abcdef", "acf"));        assertEquals("12356", Solution.lcs("132535365", "123456789"));//        assertEquals("BCBA", Solution.lcs("ABCBDAB", "BDCABA"));        long endTime=System.nanoTime(); //获取结束时间          System.out.println("程序运行时间: "+(endTime-startTime)+"ns");     }

方法一:
Brute-Force算法(暴力枚举):
X和Y的所有子序列都检查过后即可求出X和Y的最长公共子序列。X的一个子序列相应于下标序列{1, 2, …, m}的一个子序列,因此,X共有2m个不同子序列(Y亦如此,如为2^n),从而穷举搜索法需要指数时间(2^m * 2^n)。

class Solution {    public static String lcs(String x, String y) {        if(x.length()<y.length()){            String z=x;            x=y;            y=z;        }        String maxstr="";        for(int i=0;i<y.length();i++){            StringBuilder strB=new StringBuilder();            int a=x.indexOf(y.charAt(i));            if(a==-1){                continue;            }else{                strB.append(y.charAt(i));                x=x.substring(a);                for(int j=i+1;j<y.length();j++){                    a=x.indexOf(y.charAt(j));                    if(a==-1){                        continue;                    }else{                        strB.append(y.charAt(j));                        x=x.substring(a);                    }                }            }            if(strB.length()>maxstr.length()){                maxstr=strB.toString();            }        }        return maxstr;    }}

方法二:
LCS 最大公共序列算法
聪明的程序员想到了,一个用矩阵来查找的算法,就是把两个队列用整形矩阵表示, 相同的为1, 不同的为0, 然后求最大对角线,优化是优化了很多, 不过求最大对角线也不省心。

聪明的程序员再次优化了算法,就是相同的不是用1表示, 而是数字叠加,只需求最后一行前后数字查为1的列的对应字母(也可求最大对角线),时间复杂度也降到了 O(mn)+O(m+n)
比如求 x = “acf” and y = “abcdef”

      a  b  c  d  e  f  [0, 0, 0, 0, 0, 0, 0]a [0, 1, 1, 1, 1, 1, 1]b [0, 1, 1, 2, 2, 2, 2]c [0, 1, 1, 2, 2, 2, 3]
 class Solution {    public static String lcs(String x, String y) {        // your code here                int m = x.length(), n = y.length();        int[][] nums = new int[m + 1][n + 1];        for (int i = 1; i <= m; i++) {            for (int j = 1; j <= n; j++) {                nums[i][j] = nums[i - 1][j - 1] + (x.charAt(i - 1) == y.charAt(j - 1) ? 1 : 0);                nums[i][j] = Math.max(nums[i][j], nums[i - 1][j]);                nums[i][j] = Math.max(nums[i][j], nums[i][j - 1]);            }        }        StringBuilder sb = new StringBuilder();        for(int i = 1; i <= n; i++) {            if (nums[m][i] - nums[m][i - 1] == 1) {                sb.append(y.charAt(i - 1));            }        }        return sb.toString();    }}

方法三:
动态规划算法
递归

记:
Xi=﹤x1,⋯,xi﹥ 即X序列的前 i 个字符 (1≤i≤m)(前缀)
Yj=﹤y1,⋯,yj﹥ 即Y序列的前 j 个字符 (1≤j≤n)(前缀)
假定
Z=﹤z1,⋯,zk﹥∈ LCS(X , Y)

  • 若xm=yn(最后一个字符相同),则不难用反证法证明:该字符必是X与Y的任一最长公共子序列Z(设长度为k)的最后一个字符,即有zk = xm = yn 且显然有Zk-1∈LCS(Xm-1 , Yn-1)即Z的前缀Zk-1是Xm-1与Yn-1的最长公共子序列。此时,问题化归成求Xm-1与Yn-1的LCS(LCS(X , Y)的长度等于LCS(Xm-1 , Yn-1)的长度加1)。
  • 若xm≠yn,则亦不难用反证法证明:要么Z∈LCS(Xm-1, Y),要么Z∈LCS(X , Yn-1)。由于zk≠xm与zk≠yn其中至少有一个必成立,若zk≠xm则有Z∈LCS(Xm-1 , Y),类似的,若zk≠yn 则有Z∈LCS(X , Yn-1)。此时,问题化归成求Xm-1与Y的LCS及X与Yn-1的LCS。LCS(X , Y)的长度为:max{LCS(Xm-1 , Y)的长度, LCS(X , Yn-1)的长度}。

另外两个序列的LCS中包含了两个序列的前缀的LCS,故问题具有最优子结构性质考虑用动态规划法。

最长公共子序列的结构有如下表示:

设序列X=< x1, x2, …, xm > 和 Y=< y1, y2, …, yn > 的一个最长公共子序列 Z=< z1, z2, …, zk >,则:

  1. 若xm=yn,则zk=xm=yn且Zk-1是Xm-1和Yn-1的最长公共子序列;
  2. 若xm≠yn且zk≠xm ,则Z是Xm-1和Y的最长公共子序列;
  3. 若xm≠yn且zk≠yn ,则Z是X和Yn-1的最长公共子序列。

其中Xm-1=< x1, x2, …, xm-1 >,Yn-1=< y1, y2, …, yn-1 >,Zk-1=< z1, z2, …, zk-1 >。

但是这个发现费时很高~

public class Solution {    public static String lcs(String x, String y) {       if (x.length() == 0 || y.length() == 0)          return "";       String xlast=x.substring(x.length()-1);       String ylast=y.substring(y.length()-1);       if (xlast.equals(ylast))         return lcs( x.substring(0,x.length()-1), y.substring(0,y.length()-1) ) + ylast;       String lcsA = lcs( x, y.substring(0,y.length()-1) );       String lcsB = lcs( x.substring(0,x.length()-1), y );       return (lcsA.length() > lcsB.length() ? lcsA : lcsB);    }}
原创粉丝点击