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长度

    不求长度求原序列

    参考

  • 相关阅读:
    OnSharedPreferenceChangeListener不被调用原理及解决方案
    自测题的整理(持续更新)
    排序的练习题
    C++各种类型的简单排序大汇总~
    洛谷P1540 机器翻译
    队列
    文件和结构体
    洛谷P1079 Vigenère 密码
    洛谷P1328生活大爆炸版石头剪刀布
    洛谷P1553数字反转升级版
  • 原文地址:https://www.cnblogs.com/qbits/p/11230679.html
Copyright © 2011-2022 走看看