LCIS-最长公共上升子序列
以
f
[
i
]
[
j
]
f[i][j]
f[i][j] 表示: 在序列
a
a
a 中的前
i
i
i 个字符和序列
b
b
b 中的前
j
j
j 个字符中选出的结尾为
b
[
j
]
b[j]
b[j] 的最长公共上升子序列的值;
若
a
[
i
]
!
=
b
[
j
]
a[i]!=b[j]
a[i]!=b[j],则
f
[
i
]
[
j
]
=
f
[
i
−
1
]
[
j
]
f[i][j]=f[i-1][j]
f[i][j]=f[i−1][j],序列
a
a
a 只能在前
i
−
1
i-1
i−1 个字符中选取了;
若
a
[
i
]
=
b
[
j
]
a[i]=b[j]
a[i]=b[j],则
f
[
i
]
[
j
]
=
max
1
≤
k
<
j
b
[
k
]
<
b
[
j
]
=
a
[
i
]
f
[
i
−
1
]
[
k
]
+
1
;
f[i][j]=\max_{\begin{array}{c} 1\le k<j\\b[k]<b[j]=a[i] \end{array}} f[i-1][k]+1 ;
f[i][j]=1≤k<jb[k]<b[j]=a[i]maxf[i−1][k]+1;这个式子的意思即为考虑LCIS中的倒数第二个元素,假设为
b
[
k
]
b[k]
b[k],只要它满足上升的条件(即
b
[
k
]
<
b
[
j
]
b[k]<b[j]
b[k]<b[j])就可以用它来更新;最后的
+
1
+1
+1 意为
a
[
i
]
=
b
[
j
]
a[i]=b[j]
a[i]=b[j] 这一位公共字符的长度。
最后,其答案即为 result = max 0 ≤ i ≤ n f [ n ] [ i ] ; \text{result}=\max_{0\le i\le n}f[n][i]; result=0≤i≤nmaxf[n][i];取到0时即为序列 b b b 中一个都选不出来(没有公共部分)。
其核心代码如下:
for(int i=1;i<=n;i++)
for(int j=1;j<=n;j++)
{
f[i][j]=f[i-1][j];
if(a[i]==b[j])
{
for(int k=1;k<j;k++)
if(b[k]<b[j])f[i][j]=max(f[i][j],f[i-1][k]+1);
}
}
int res=0;
for(int i=0;i<=n;i++)res=max(res,f[n][i]);
在这样的朴素算法中,需要三重循环,时间复杂度 O ( n 3 ) O(n^3) O(n3);然而再最后一重循环中,有许多重复的计算,可以优化掉:
对于一个固定的 i i i , 在第三重循环下的判断条件中, b [ k ] < b [ j ] b[k]<b[j] b[k]<b[j] 等价于 b [ k ] < a [ i ] b[k]<a[i] b[k]<a[i];这个条件与 j j j 无关。
所以对于某个下标
j
j
j ,第三重循环等于在计算:当
k
k
k 为一个比当前的
j
j
j 要小的下标时,满足
b
[
k
]
<
a
[
i
]
b[k]<a[i]
b[k]<a[i] 的状态(由于
i
i
i 固定所以
a
[
i
]
a[i]
a[i] 为定值);
而当当前的
j
j
j 遍历到下一个值(即
j
+
1
j+1
j+1)时,第三重循环仍然是在计算:当
k
k
k 为一个比当前
j
+
1
j+1
j+1 要小的下标时,满足
b
[
k
]
<
a
[
i
]
b[k]<a[i]
b[k]<a[i] 的状态。
对比发现,当
j
j
j 增加时,在第三重循环的计算中只需要多比较一次
k
=
j
k=j
k=j 时的
f
[
i
−
1
]
[
k
]
f[i-1][k]
f[i−1][k] 的状态的值来取出最大的那个即可;然而,实际的朴素程序中重新做了一次循环遍历来计算最大值。
因此,可以维护一个变量
tmp
\text{tmp}
tmp ,在每次
j
j
j 循环时更新它的值即可,从而消除第三层循环,使复杂度变为平方级别。
更新后的核心代码如下:
for(int i=1;i<=n;i++)
{
int tmp=0;
for(int j=1;j<=n;j++)
{
f[i][j]=f[i-1][j];
if(b[j]==a[i])f[i][j]=max(f[i][j],tmp+1); // tmp 对应原先第三重循环所遍历出来的最大的 f[i-1][k]
if(b[j]<a[i])tmp=max(tmp,f[i-1][j]); // 每当 j 增加一个,只要满足 b[j]<a[i] 的条件就要维护 tmp 的值
}
}
int res=0;
for(int i=0;i<=n;i++)res=max(res,f[n][i]);