📜  门|门 IT 2006 |第 51 题(1)

📅  最后修改于: 2023-12-03 15:28:48.236000             🧑  作者: Mango

门|门 IT 2006 |第 51 题

这是一道经典的计算机科学问题,它涉及到编写一个程序,找到一个长度为n的数字序列的最长严格递增子序列。该问题被证明可以在O(nlogn)时间复杂度内解决,使用动态规划和二分查找的技巧。

动态规划思路

最长递增子序列(LIS)问题可以使用动态规划的思想来解决。我们可以定义一个长度为n的数组dp,其中dp[i]表示以第i个数字为结尾的最长严格递增子序列的长度。因此,我们可以通过以下方式计算dp[i]:

对于每个i < j,如果 nums[j] > nums[i],则dp[j]的值应该是dp[i] + 1。

这是因为j的最长递增子序列应该是i的最长递增子序列加上j。

因此,当我们计算dp[j]的值时,我们可以遍历0到j-1的所有数字i,如果nums[j]>nums[i],则dp[j]应该取dp[i]+1和dp[j]中的较大值。最终结果应该是dp数组中的最大值。

二分查找优化

我们可以通过二分查找来优化时间复杂度。具体来说,对于每个数字nums[i],我们可以将其插入到一个有序的序列中,该序列是之前计算过的最长递增子序列。我们可以维护一个数组tails,其中tails[k]表示长度为k的最长递增子序列的结尾数字。例如,如果tails = [4,5],则长度为1和2的最长递增子序列分别为[4]和[4,5]。

当我们遍历每个数字nums[i]时,我们可以使用二分查找来找到其中一个最长递增子序列,使得该字串可以在其结尾添加nums[i]而不破坏严格递增性。然后我们可以将nums[i]添加到该子序列的末尾,或者将其替换为tails中下一个更大的数字。

最长递增子序列的长度就是tails的长度。

返回Markdown格式代码片段

下面的代码片段演示了如何实现一个O(nlogn)算法来解决最长递增子序列问题。代码使用动态规划和二分查找来优化时间复杂度。返回的代码是markdown格式,用于更好地展示代码。

def lengthOfLIS(nums: List[int]) -> int:
    tails = [0] * len(nums)
    size = 0
    for num in nums:
        lo, hi = 0, size
        while lo < hi:
            mid = (lo + hi) // 2
            if tails[mid] < num:
                lo = mid + 1
            else:
                hi = mid
        tails[lo] = num
        size = max(size, lo + 1)
    return size
int lengthOfLIS(vector<int>& nums) {
    vector<int> tails;
    tails.push_back(nums[0]);
    for (int i = 1; i < nums.size(); i++){
        if(nums[i] > tails.back()) tails.push_back(nums[i]);
        else {
            int index = lower_bound(tails.begin(), tails.end(), nums[i]) - tails.begin();
            tails[index] = nums[i];
        }
    }
    return tails.size();
}
public int lengthOfLIS(int[] nums) {
    int[] tails = new int[nums.length];
    int size = 0;
    for (int num : nums) {
        int lo = 0, hi = size;
        while (lo < hi) {
            int mid = lo + (hi - lo) / 2;
            if (tails[mid] < num) {
                lo = mid + 1;
            } else {
                hi = mid;
            }
        }
        tails[lo] = num;
        size = Math.max(size, lo + 1);
    }
    return size;
}
function lengthOfLIS(nums: number[]): number {
    let tails = new Array(nums.length).fill(0)
    let size = 0
    for(let num of nums) {
        let i = 0, j = size
        while(i < j) {
            let mid = (i + j) >> 1
            if(tails[mid] < num) {
                i = mid + 1
            } else {
                j = mid
            }
        }
        tails[i] = num
        if(i === size) size++
    }
    return size
}
function lengthOfLIS(nums: number[]): number {
    let tails = new Array(nums.length).fill(0)
    let size = 0
    for(let num of nums) {
        let i = 0, j = size
        while(i < j) {
            let mid = (i + j) >> 1
            if(tails[mid] < num) {
                i = mid + 1
            } else {
                j = mid
            }
        }
        tails[i] = num
        if(i === size) size++
    }
    return size
}