题目描述
难度分:1600
输入n(1≤n≤105)和长为n的数组a(1≤a[i]≤109)。
你可以修改a中的至多一个数(修改成任意整数)。输出a的最长严格递增子数组的长度。注:子数组是连续的。
输入样例
6
7 2 3 1 5 6
输出样例
5
算法
前后缀分解
预处理出pre和suf两个数组,pre[i]表示以a[i]结尾的最长严格递增子数组长度,suf[i]表示以a[i]开头的最长严格递增子数组长度。状态转移如下:
- 如果a[i]>a[i−1],那么pre[i]=pre[i−1]+1,表示a[i]可以接在a[i−1]结尾的最长递增子数组后面;否则pre[i]=1,表示a[i]自成一个严格递增子数组。
- 同理如果a[i]<a[i+1],那么suf[i]=suf[i+1]+1,否则suf[i]=1。
如果一个数都不修改,那么答案就是maxi∈[1,n]pre[i]。
否则枚举修改的位置i,添加两个哨兵a[0]=0,a[n+1]=2×109。如果要修改a[i],则a[i−1]+1<a[i+1]时可以把a[i]修改为(a[i−1]+1,a[i+1])中的一个数,从而把a[i−1]结尾的最长递增子数组和a[i+1]开头的最长递增子数组连接起来变成一个长度为pre[i−1]+suf[i+1]+1的最长递增子数组。否则就只能将a[i−1]结尾的最长递增子数组或a[i+1]开头的最长递增子数组加长1。
所有i位置答案的最大值以及maxi∈[1,n]pre[i]中较大的那个就是最终答案。
复杂度分析
时间复杂度
O(n)遍历可以求出pre和suf两个数组。遍历修改的位置i∈[1,n],每个位置求答案的时间复杂度为O(1),时间复杂度为O(n)。因此,整个算法的时间复杂度为O(n)。
空间复杂度
除了输入的a数组,空间消耗主要在于pre和suf两个数组,因此额外空间复杂度为O(n)。
C++ 代码
#include <bits/stdc++.h>
using namespace std;
const int N = 200010;
int n, a[N], pre[N], suf[N];
int main() {
scanf("%d", &n);
int ans = 1;
for(int i = 1; i <= n; i++) {
scanf("%d", &a[i]);
pre[i] = a[i] > a[i - 1]? pre[i - 1] + 1: 1;
ans = max(ans, pre[i]);
}
a[n + 1] = 2e9;
suf[n + 1] = 0;
for(int i = n; i >= 1; i--) {
suf[i] = a[i] < a[i + 1]? suf[i + 1] + 1: 1;
}
for(int i = 1; i <= n; i++) {
ans = max(ans, max(pre[i - 1], suf[i + 1]) + 1);
if(a[i - 1] + 1 < a[i + 1]) {
ans = max(pre[i - 1] + suf[i + 1] + 1, ans);
}
}
printf("%d\n", ans);
return 0;
}