关于LIS优化
2026-10-06 17:40:25
发布于:广东
朴素 DP(O (n²))
dp[i]表示以i结尾的最大上升子序列长度
for(int i=1;i<=n;i++){
dp[i]=1;
for(int j=1;j<=i-1;j++){
if(a[i]>a[j]){
dp[i]=max(dp[i],dp[j]+1);
}
}
}
太慢了。。
我们可不可以反过来想?
dp[i]表示最长子序列长度为i的最小的末尾元素
比如a = [ 4, 2, 3, 1, 5 ,6 ,3 ]
- 开始时4 dp[1]=4 [4]
- 2加入 dp[1]=2 [2]
- 3 dp[2]=3 (3比2大,可以在末尾加)[2,3]
- 1 dp[1]=1 [1,3]
- 5 dp[3]=5 [1,3,5]
- 6 dp[4]=6 [1,3,5,6]
- 3 dp[3]=3 [1,3,3,6]
我们在遍历过程中在找dp中元素比自己小的然后加进去
比如再加入第2个3时,dp数组是这样的
dp=[1,3,5,6](我们可以发现它是具有单调性的,优化点就在这,进行二分)
我们找到第一个≥3的也就是dp[3]=5
再进行替换dp[3]=3
vector<int> g;
for(int i=1;i<=n;i++){
auto it = lower_bound(g.begin(), g.end(), a[i]);
if(it == g.end()){
g.push_back(a[i]);
}
else{
*it = a[i];
}
}
cout << g.size() << endl;
最后的时间复杂度为O(nlogn)
然后这个优化也可以用来优化LCS(仅限其中一个字符串太长,就比如n<=100,m<=1e6)
这里只写大概思路:
dp[i]为匹配了i对后最靠前的匹配完了的坐标
O(n* l * logm)这个算法叫 Hunt‑Szymanski 算法
全部评论 4
LIS 不是被爆标了吗 /yiw
昨天 来自 浙江
0哦我记错了
昨天 来自 浙江
0
3
昨天 来自 浙江
0就算f[i]表示以a[i]结尾也可以权值线段树达到n log n吧。
大不了再离散化一下
昨天 来自 广东
0是的,但我觉得这种方法最好想也最好写了,毕竟线段树也不是人人都能打出来的嘛(比如我),开头我只是说朴素的写法不包含其他优化,不过用词不当实在抱歉!
昨天 来自 广东
0感谢纠错,以后发学术贴我会再次斟酌用词
昨天 来自 广东
0貌似树状数组也可以
昨天 来自 广东
0
好像在哪里见过,但是为啥说是“反过来想啊”
昨天 来自 广东
0坏了,好像这个挺经典的,我是
傻
子
昨天 来自 广东
0这里反过来想的意思大概是dp的下标和存值像反过来了一样,不过不是标准的,只是更好理解,因为我关于此优化没有经过系统性的学习,此贴以后还会做优化
昨天 来自 广东
0加油
昨天 来自 广东
0

























有帮助,赞一个