zoukankan      html  css  js  c++  java
  • 求最长非降(递增)子序列LIS的长度,及注意事项

    非降序列(Increasing Sequence)例如:

    (1) 完全递增型序列:S={1,3,6,7,9}

    (2) 部分存在等于的序列:S={1,3,3,6,9}

    S的非降子序列:由原序列S的元素组成的(且保持元素之间的顺序不变的)组成的序列。

    例如S={5,4,7,1,8,6}的子序列包括:

    1个元素的:{5},{4},{7},{1},{8},{6} 等6个子序列

    2个元素的:{5,7},{5,8},{5,6},{4,7},{4,8},{4,6},{7,8},{1, 8},{1,6} 等

    3个元素的:{5,7,8},{4,7,8}

    最长非降子序列(Longest Increasing Sequences,LIS),是S的所有非降子序列中包含元素最多的那些子序列,例如上述序列的最长子序列为{5,7,8}和{4,7,8}

    而最长非降子序列的长度就是最长非降子序列的元素个数,例如上例中的最长非降子序列的长度为3。

    求解序列S的最长非降子序列的长度通常采用动态规划算法:

    设d(i)为前i个元素的最长非降子序列的长度,则d(i)=max{ d(j)+1}, 其中 0<=j<=i,且S[j]<S[i]。

    然后写出类似这样的代码:

    public static void LIS(int []S)
        {
            int []d=new int[S.length];
            
            for(int i=0;i<S.length;i++)
            {
                d[i]=1;
                for(int j=0;j<i;j++)
                {
                    if(S[j]<=S[i] && d[j]+1<d[i])
                    {
                        d[i]=d[j]+1;
                    }    
                }
            }
            
            for(int i=0;i<S.length;i++)
            {
                System.out.print(d[i]+" ");
            }
        }

    然后,调用该LIS函数,求解。如

    int[]seq={5,3,4,8,6,7};
    LIS(seq);

    会输出:1 1 2 3 3 4 ,代表包含前1个元素的LIS长度为1,包含前2个元素的子序列的LIS长度为1,包含前3个元素的子序列的LIS长度为2,。。。

    需要注意的是:上述递推公式d(i)=max{ d(j)+1}, 其中 0<=j<=i,且S[j]<S[i],只适用于以S[i]作为结尾元素的LIS序列。

    而如果去掉以S[i]作为结尾元素这个条件,就不能这么求解。

    例如int [ ]seq={5,4,7,1,8,6 }, 用上述程序求解,会输出1 1 2 1 3 2 (即d[3]=1 ,d[5]=2,分别代表包含前4个元素子序列的LIS长度为1,包含前6个元素子序列的LIS长度为2)。但其实包含前4个元素的子序列(即{5,4,7,1})的LIS的长度应该是2,即,d[3]=2, LIS={5,7}或{4,7}, 而 d[5] 应该是3。

    问题出在,上述的程序要求LIS以S[i]作为结尾,即LIS的最大元素为S[i],这时d[i]=max{d[j],其中0<=j<=i }是没错的,但如果去掉这个限制条件,就用上面的程序求解就会出错。

    那么,如何求解序列S的最长非降子序列的长度(不要求以S的最后一个元素作为LIS的最大元素)?

    这通常需要转化为最长公共子序列(LCS)的问题。即:序列S的LIS= 将S排序后的有序序列S' 与 原序列S 的最长公共子序列。

    例如,S={5,4,7,1,8,6 } 的LIS为{ 4,7,8 }或{ 5,7,8 },长度为3

    对S排序后的S'={ 1,4,5,6,7,8}, S‘与 S的最长公共子序列就是{4, 7, 8 }和{ 5,7,8 }

    所以,如何求解序列S的最长非降子序列的长度,首先需要进行排序,然后求LCS的长度即可。

    示例代码:

    public static void main(String[]args)
    {
        Scanner sc=new Scanner(System.in);
        int n=sc.nextInt();
        int []peaches=new int[n];
        for(int i=0;i<n;i++)
        {
            peaches[i]=sc.nextInt();
        }
        int []copy=peaches.clone();
        quickSort(peaches,0,n-1);
        int lisLength=LCS(peaches,copy)
        System.out.println("LIS length is"+lisLength);
    }
    
    public static void quickSort(int []arr,int start,int end)
    {
        if(start>=end)
            return;
        int i=start;
        int j=end;
        int key=arr[i];
        while(i<j)
        {
            while(i<j&&key<=arr[j])
            {
                j--;
            }
            arr[i]=arr[j];
            while(i<j&&key>arr[i])
            {
                i++;
            }
            arr[j]=arr[i];
        }
        arr[i]=key;
        quickSort(arr,start,i-1);
        quickSort(arr,i+1,end);
    }
    
    public static int LCS(int[]a,int[] b)
    {
        int [][]c=new int[a.length+1][b.length+1];
        for(int i=1;i<=a.length;i++)
        {
            for(int j=1;j<=b.length;j++)
            {
                if(a[i-1]==b[j-1])
                    c[i][j]=c[i-1][j-1]+1;
                else
                    c[i][j]=Math.max(c[i-1][j], c[i][j-1]);
            }
        }
        return c[a.length][b.length];
    }

    如何求LCS的长度?看这里

  • 相关阅读:
    面试精选:链表问题集锦
    经典排序算法总结与实现 ---python
    Python高级编程–正则表达式(习题)
    Python面试题汇总
    Python正则表达式
    Linux下的Libsvm使用历程录
    在 linux(ubuntu) 下 安装 LibSVM
    过拟合
    百度历年笔试面试150题
    MATLAB 的数据类型
  • 原文地址:https://www.cnblogs.com/aaronhoo/p/7368082.html
Copyright © 2011-2022 走看看