zoukankan      html  css  js  c++  java
  • 最长回文子序列LCS,最长递增子序列LIS及相互联系

    最长公共子序列LCS

    Lintcode 77. 最长公共子序列
    LCS问题是求两个字符串的最长公共子序列

    [ dp[i][j] = left{egin{matrix} & max(dp[i-1][j], dp[i][j-1]), s[i] != s[j]\ & dp[i-1][j-1] + 1, s[i] == s[j] end{matrix} ight. ]

    许多问题可以变形为LCS问题以求解

    class Solution {
    public:
        /**
         * @param A: A string
         * @param B: A string
         * @return: The length of longest common subsequence of A and B
         */
        int longestCommonSubsequence(string &A, string &B) {
            // write your code here
            int n = A.size();
            int m = B.size();
            std::vector<vector<int>> dp(m+1, vector<int>(n+1, 0)) ;
            for(int i = 1; i <= m; i++){
                for(int j = 1; j <= n; j++){
                   
                    if(A[i-1] == B[j-1]) dp[i][j] = dp[i-1][j-1] + 1;
                    else dp[i][j] = max(dp[i-1][j], dp[i][j-1]);
                  
                }
            }
            
            return dp[m][n];
        }
    };
    

    因为dp[i][j]仅仅用到了i-1和i层的数据,因此可以用滚动数组来压缩空间,使得空间复杂度为(O(min(m,n)))

    class Solution {
    public:
        /**
         * @param A: A string
         * @param B: A string
         * @return: The length of longest common subsequence of A and B
         */
        int longestCommonSubsequence(string &A, string &B) {
            // write your code here
            int n = A.size();
            int m = B.size();
            std::vector<vector<int>> dp(2, vector<int>(n+1, 0)) ;
            for(int i = 1; i <= m; i++){
                for(int j = 1; j <= n; j++){
                   
                    if(A[i-1] == B[j-1]) dp[i%2][j] = dp[(i-1)%2][j-1] + 1;
                    else dp[i%2][j] = max(dp[(i-1)%2][j], dp[i%2][j-1]);
                  
                }
            }
            
            return dp[m%2][n];
        }
    };
    

    最长递增子序列LIS

    300. Longest Increasing Subsequence

    动态规划

    可以假定dp[i]为以nums[i]结尾的LIS长度,则dp[i] = max(dp[j] + 1)( j<i 且 nums[j] < nums[i]), 时间复杂度为(O(n^2)),时间复杂度为(O(n))

    class Solution {
    public:
        int lengthOfLIS(vector<int>& nums) {
            int n = nums.size();
            vector<int> dp(n, 1);
            int MAX = 0;
            for(int i = 0; i < n; i++){
                for(int j = 0; j < i; j++){
                    if(nums[i] > nums[j]) dp[i] = max(dp[j] + 1, dp[i]);
                }
                MAX = max(MAX, dp[i]);
            }
            
            return MAX;
        }
    };
    

    贪心+二分

    首先我们设置一个辅助数组v,其中v[i]表示长度为i-1的LIS的末尾值,首先扫描原数组,当处理到nums[i]时和v中的数据比较,二分查找最后一个比nums[i]小的值,并更换,如果不存在,则加入到末尾,v最后的长度就是原数组LIS的长度,时间复杂度(nlgn)

    class Solution {
    public:
        int lengthOfLIS(vector<int>& nums) {
            int n = nums.size();
            vector<int> v;
            
            for(int i = 0; i < n; ++i){
                auto loc = lower_bound(v.begin(), v.end(), nums[i]);
                if(loc == v.end()) v.push_back(nums[i]);
                else *loc = nums[i];
            }
            
            return v.size();
        }
    };
    

    如果仅仅是求LIS长度和允许改变原数组,空间复杂度可降低为(O(1))

    class Solution {
    public:
        int lengthOfLIS(vector<int>& nums) {
            int n = nums.size();
            auto p = nums.begin();
            auto q = nums.begin();
            
            for(int i = 0; i < n; i++){
                auto r = lower_bound(p, q, nums[i]);
                if(r == q){ ++q; }
                *r = nums[i];
            }
            
            return q - p;
        }
    };
    

    LIS与LCS的相互转化

    LIS问题可以变形为LCS问题,如输入数组为[5,1,4,2,3],最长递增子序列为[1,2,3],可以先将原数组排序得到一个新数组[1,2,3,4,5],然后新数组与原数组作为LCS的输入求解, 时间复杂度为(O(n^2)), 空间复杂度为(O(n^2))

    LCS问题也可变为LIS问题,假定输入数组为数字数组如A=[1,7,5,4,8,3,9], B=[1,4,3,5,6,2,8,9],且在A,B两个序列中每个元素各不相同(如1-n的排列),如果使用LCS求解最长公共子序列长度,则复杂度为(O(n^2)),A,B两个序列中每个元素各不相同,因此我们可以将A重新编码A=[1,2,3,4,5,6,7](编码不重复), B可以编码为B=[1,4,6,3,0,0,5,7](0表示不存在,也可以直接删除),然后求重新编码后A,B的LIS长度,时间复杂度为(O(nlgn))

    LCS与最长回文子序列LPS及变种

    求S的最长回文子序列也可以使用LCS的思想,先将S反转得到S',然后求LCS(S,S')
    leetcode 516. Longest Palindromic Subsequence

    class Solution {
    public:
        int lcs(string &A, string &B) {
            int n = A.size();
            int m = B.size();
            std::vector<vector<int>> dp(m+1, vector<int>(n+1, 0)) ;
            for(int i = 1; i <= m; i++){
                for(int j = 1; j <= n; j++){
                   
                    if(A[i-1] == B[j-1]) dp[i][j] = dp[i-1][j-1] + 1;
                    else dp[i][j] = max(dp[i-1][j], dp[i][j-1]);
                  
                }
            }
            
            return dp[m][n];
        }
        int longestPalindromeSubseq(string s) {
            string t(s.rbegin(), s.rend()); 
            return lcs(s, t);
        }
    };
    

    变种:求在S中任何位置插入或删除最少字符个数使得S成为回文串
    解法:先求最长回文子序列,然后用原长度-LPS长度

    不求长度求原序列

    参考

  • 相关阅读:
    监控里的主码流和子码流是什么意思
    监控硬盘容量计算
    一个能让你了解所有函数调用顺序的Android库
    电工选线
    oracle linux dtrace
    list all of the Oracle 12c hidden undocumented parameters
    Oracle Extended Tracing
    window 驱动开发
    win7 x64 dtrace
    How to Use Dtrace Tracing Ruby Executing
  • 原文地址:https://www.cnblogs.com/qbits/p/11230679.html
Copyright © 2011-2022 走看看