算法->动态规划之LCS算法

244 阅读3分钟

携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第21天,点击查看活动详情

动态规划(DP)是什么

动态规划(dynamic programming)是运筹学的一个分支,是求解决策过程(decision process)最优化的数学方法。

20世纪50年代初美国数学家R.E.Bellman等人在研究多阶段决策过程(multistep decision process)的优化问题时,提出了著名的最优化原理(principle of optimality),把多阶段过程转化为一系列单阶段问题,利用各阶段之间的关系,逐个求解,创立了解决这类过程优化问题的新方法——动态规划。

动态规划的特点

动态规划最核心的思想,就在于拆分子问题,记住过往,减少重复计算。

分析是从大到小,写代码是从小到大。计算过程中会把结果都记录下,最终结果在记录中找到

LCS(最长公共子序列)

一个序列A任意删除若干个字符得到新序列B,则A叫做B的子序列

两个序列X和Y的公共子序列中,长度最长的那个,定义为X和Y的最长公共子序列

最长公共子序列(LCS)-〉 是一个在一个序列集合中(通常为两个序列)用来查找所有序列中最长子序列的问题。这与查找最长公共子串的问题不同的地方是:子序列不需要在原序列中占用连续的位置。而最长公共子串(要求连续)和最长公共子序列是不同的

LCS的应用场景

  • 相似度的比较:计算生物学DNA对比(亲子验证),,,百度云盘找非法数据
  • 图形相似处理,媒体流的相似比较,百度知道,百度百科,,WEB页面中找非法言论

LCS的解决办法

  1. 穷举法(实际应用中不可取)
  2. 动态规划

动态归法算法解决LCS的思路:

以两个序列 X、Y 为例子:

设有二维数组f[i,j] 表示 X 的 i 位和 Y 的 j 位之前的最长公共子序列的长度,则有:

f[1][1] = same(1,1);

f[i,j] = max{f[i-1][j -1] + same(i,j),f[i-1,j],f[i,j-1]};

其中,same(a,b)当 X 的第 a 位与 Y 的第 b 位相同时为“1”,否则为“0”。

此时,二维数组中最大的数便是 X 和 Y 的最长公共子序列的长度,依据该数组回溯,便可找出最长公共子序列。

代码编写

 /**
     * LCS算法
     */
    public static int max(int a,int b){
        return (a>b)?a:b;
    }
    public static void getLCS(String x,String y){
        char[] s1=x.toCharArray();
        char[] s2=y.toCharArray();
        int[][] array=new int[x.length()+1][y.length()+1];
        //先把第一行和第一列填上零
        for (int i = 0; i < array[0].length; i++) {
            array[0][i]=0;
        }
        for (int i = 0; i < array.length; i++) {
            array[i][0]=0;
        }
        //使用动态规划的方式填入数据
        for (int i = 1; i < array.length; i++) {
            for (int j = 1; j < array[i].length; j++) {
                if(s1[i-1]==s2[j-1]){//如果相等,左上角加1填入
                    array[i][j]=array[i-1][j-1]+1;
                }else{
                    array[i][j]=max(array[i-1][j],array[i][j-1]);
                }
            }
        }
        //打印
        for (int i = 0; i < array.length; i++) {
            for (int j = 0; j < array[i].length; j++) {
                System.out.print(array[i][j]+" ");
            }
            System.out.println();
        }

        //从后往前找到结果
        Stack result=new Stack();
        int i=x.length()-1;
        int j=y.length()-1;
        while((i>=0) && (j>=0)){
            if(s1[i]==s2[j]){
                result.push(s1[i]);
                i--;
                j--;
            }else{//注意数组和String中的位置有一位差
                if(array[i+1][j+1-1]>array[i+1-1][j+1]){
                    j--;
                }else{
                    i--;
                }
            }
        }
        System.out.println("-----");
        while(!result.isEmpty()){
            System.out.println(result.pop()+" ");
        }
    }