题意, 给一串字符串, 问删掉一段 L 长度的子串之后, 剩下的串的 LIS 最大长度。
做法,首先先用 O(n log n) 的方法二分求出原串的 LIS, 然后用 dp 求出一段子串中间去掉 L 长度后的 LIS。
用 LIS { Str } 表示串 Str 的 LIS, S [ l, r ] 表示子串 S(l ~ r)
则转移方程可以表示为: dp[i] = max( LIS { S [ 0, i - L ] + S [ i, i ] } , dp [ pos ] + LIS { S [ pos , i ] } ) (L < pos <= i )
其中 dp 部分也可以沿用求 LIS 的二分思想求出 。
参考代码:
#include <bits/stdc++.h>
using namespace std;
const int N = 100005;
int a[N], b[N], f[N], c[N], d[N], g[N], CASE;
int main() {
int T;
scanf("%d", &T);
while (T--) {
int n, L;
scanf("%d %d", &n, &L);
for (int i = 1; i <= n; i++) scanf("%d", a + i);
a[++n] = 1e9 + 10;
int k = 0, h = 0, w = 0;
memset(b, 0x3f, sizeof b);
memset(c, 0x3f, sizeof c);
memset(d, 0x3f, sizeof d);
b[0] = c[0] = d[0] = - 1e9 - 10;
for (int i = 1; i <= n; i++) {
f[i] = lower_bound(b, b + k + 2, a[i]) - b;
b[f[i]] = min(b[f[i]], a[i]);
if (f[i] > k) k = f[i];
if (i <= L) continue;
int x = lower_bound(d, d + w + 2, a[i]) - d;
int y = lower_bound(c, c + h + 2, a[i]) - c;
g[i] = max(x, y);
d[g[i]] = min(d[g[i]], a[i]);
if (g[i] > w) w = g[i];
int p = f[i - L];
c[p] = min(c[p], a[i - L]);
d[p] = min(c[p], d[p]);
if (p > h) h = p;
}
printf("Case #%d: %d\n", ++CASE, w - 1);
}
}