You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何高效查找最长递增子序列(LIS)的全部对应子序列?

问题:查找所有最长递增子序列(LIS)的高效优化方案

给定数组序列,需找出所有长度等于最长递增子序列(LIS)长度的递增子序列。例如数组[1, 3, 2, 5, 2]应输出:1 3 5 和 1 2 5。

当前已实现的LIS长度计算函数基于O(nlogn)的动态规划思路,效率极高,但查找所有对应子序列的函数在数组规模达1000+时耗时极长——该函数采用三层循环,递归实现效率同样低下。


现有代码

计算LIS长度的函数

int findLISLength(const std::vector<int>& sequence, size_t n) {
    std::vector<int> lisEnd(n + 1, 0);
    std::vector<int> parent(n, 0);
    int length = 0;

    for (int i = 0; i < n; ++i) {
        int low = 1;
        int high = length;
        while (low <= high) {
            int mid = (low + high) / 2;
            if (sequence[lisEnd[mid]] < sequence[i])
                low = mid + 1;
            else
                high = mid - 1;
        }
        int pos = low;
        parent[i] = lisEnd[pos - 1];
        lisEnd[pos] = i;

        if (pos > length)
            length = pos;
    }

    return length;
}

查找指定长度递增子序列的函数

std::vector<std::vector<int>> getAllIncreasingSubsequences(const std::vector<int>& sequence, size_t length) {
    std::vector<std::vector<int>> result;
    std::vector<std::vector<std::vector<int>>> dp(sequence.size());

    for (size_t i = 0; i < sequence.size(); ++i) {
        dp[i].push_back({ sequence[i] });
        for (size_t j = 0; j < i; ++j) {
            if (sequence[i] > sequence[j]) {
                for (const auto& subseq : dp[j]) {
                    if (subseq.size() + 1 <= length) {
                        auto newSeq = subseq;
                        newSeq.push_back(sequence[i]);
                        dp[i].push_back(newSeq);
                    }
                }
            }
        }
    }

    for (const auto& subseq : dp) {
        for (const auto& sub : subseq) {
            if (sub.size() == length) {
                result.push_back(sub);
            }
        }
    }

    return result;
}

主函数

int main(){
    size_t L = findLISLength(sequence, N);
    vector<vector<int>> 
        increasingSubsequences = getAllIncreasingSubsequences(sequence, L);
    //print subsequence
    cout << "L: " << L << endl;
    cout << "Increasing subsequences: " << endl;
    for (auto& subsequence : increasingSubsequences) {
        for (auto& element : subsequence) {
            cout << element << " ";
        }
        cout << '\n';
    }
}

优化方案

核心问题分析

现有getAllIncreasingSubsequences函数的问题在于:它存储了所有可能的递增子序列(无论长度是否接近LIS),当数组规模较大时,子序列数量呈指数级增长,导致时间和空间复杂度爆炸。

优化思路

  1. 预处理关键信息:先计算每个位置的dp_len数组,dp_len[i]表示以sequence[i]结尾的最长递增子序列长度,复用原O(nlogn)的LIS计算逻辑即可快速得到。
  2. 定向构建LIS:只保留能构成LIS的路径,从后往前逐层构建(从LIS的最后一个元素倒推到第一个元素),避免存储无关子序列。
  3. 去重优化:在构建过程中跳过重复元素,避免生成重复的子序列,减少无效计算。

优化后代码实现

1. 同时获取LIS长度和dp_len数组

#include <vector>
#include <utility>
using namespace std;

pair<int, vector<int>> getLISLengthAndDpLen(const vector<int>& sequence) {
    int n = sequence.size();
    vector<int> lisEnd(n + 1, 0);
    vector<int> dp_len(n, 0);
    int length = 0;

    for (int i = 0; i < n; ++i) {
        int low = 1, high = length;
        while (low <= high) {
            int mid = (low + high) / 2;
            if (sequence[lisEnd[mid]] < sequence[i]) {
                low = mid + 1;
            } else {
                high = mid - 1;
            }
        }
        int pos = low;
        dp_len[i] = pos;
        lisEnd[pos] = i;
        if (pos > length) {
            length = pos;
        }
    }
    return {length, dp_len};
}

2. 高效获取所有LIS

#include <vector>
#include <unordered_set>
#include <algorithm>
using namespace std;

vector<vector<int>> getAllLIS(const vector<int>& sequence, int L, const vector<int>& dp_len) {
    int n = sequence.size();
    vector<vector<int>> result;

    // 分层存储:layers[k] 存储长度为k的LIS前缀(从后往前构建)
    vector<vector<pair<int, vector<int>>>> layers(L + 1);

    // 初始化最后一层(长度为L的前缀,即LIS的最后一个元素)
    for (int i = n - 1; i >= 0; --i) {
        if (dp_len[i] == L) {
            // 去重:同一层相同值的元素只保留一个
            bool duplicate = false;
            for (const auto& p : layers[L]) {
                if (sequence[p.first] == sequence[i]) {
                    duplicate = true;
                    break;
                }
            }
            if (!duplicate) {
                layers[L].emplace_back(i, vector<int>{sequence[i]});
            }
        }
    }

    // 从L-1层到1层,逐层构建完整LIS
    for (int k = L - 1; k >= 1; --k) {
        for (const auto& p : layers[k + 1]) {
            int prev_idx = p.first;
            const vector<int>& curr_subseq = p.second;
            int last_val = sequence[prev_idx];
            unordered_set<int> used_vals; // 记录已使用的元素值,避免重复路径

            for (int j = prev_idx - 1; j >= 0; --j) {
                if (dp_len[j] == k && sequence[j] < last_val) {
                    if (used_vals.count(sequence[j])) {
                        continue;
                    }
                    used_vals.insert(sequence[j]);
                    vector<int> new_subseq = curr_subseq;
                    new_subseq.insert(new_subseq.begin(), sequence[j]);
                    layers[k].emplace_back(j, new_subseq);
                }
            }
        }

        // 对当前层的子序列去重,移除完全相同的前缀
        sort(layers[k].begin(), layers[k].end(), [&](const auto& a, const auto& b) {
            return a.second < b.second;
        });
        auto last = unique(layers[k].begin(), layers[k].end(), [&](const auto& a, const auto& b) {
            return a.second == b.second;
        });
        layers[k].erase(last, layers[k].end());
    }

    // 第一层的所有子序列就是完整的LIS
    for (const auto& p : layers[1]) {
        result.push_back(p.second);
    }
    return result;
}

3. 修改后主函数

#include <iostream>
#include <vector>
using namespace std;

int main() {
    vector<int> sequence = {1, 3, 2, 5, 2};
    auto [L, dp_len] = getLISLengthAndDpLen(sequence);
    vector<vector<int>> increasingSubsequences = getAllLIS(sequence, L, dp_len);

    cout << "L: " << L << endl;
    cout << "Increasing subsequences: " << endl;
    for (auto& subsequence : increasingSubsequences) {
        for (auto& element : subsequence) {
            cout << element << " ";
        }
        cout << '\n';
    }
    return 0;
}

优化效果说明

  • 空间优化:仅存储与LIS构建相关的子序列前缀,避免存储所有递增子序列,空间复杂度从指数级降至O(L*M)(M为LIS的数量)。
  • 时间优化:通过dp_len数组快速定位可构成LIS的元素,减少无效遍历;去重逻辑避免重复生成相同子序列,进一步降低计算量。
  • 适配大数组:针对1000+规模的数组,该方案的性能远优于原三层循环实现。

内容的提问来源于stack exchange,提问作者lukakone Konečnik

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.02 21:07:35