读懂题意不难看出这是一道最长公共子序列的裸题。
然而,事情并没有想象中的那么简单,这道题每个串最大250*250=62500,即时间复杂度最坏625002!!!即使时间限制3000MS也不可能跑过,再者,这么大也开不了二维DP数组。因此必须进行优化。
联想到背包问题中可以用滚动数组,引用到这里也可以,于是空间问题解决了,那么对于时间,该怎么优化呢?
我们定义DP状态为:
dp[i]表示p[1 ~ i]与整个q[]串所形成的的最长公共子序列。那么我们可以另开一个数组pl[],pl[i]记录数字i在q[]串中出现的位置。借此可以对时间进行优化(详见代码)
#include<cstdio>
#include<iostream>
#include<queue>
#include<cmath>
using namespace std;
const int maxn=252;
const int inf=0x3f3f3f3f;
int a[maxn*maxn],b[maxn*maxn],pl[maxn*maxn];
int dp[maxn*maxn];
int main()
{
int t,kase=0;
scanf("%d",&t);
while(t--)
{
int n,p,q;
scanf("%d%d%d",&n,&p,&q);
++p; ++q;
for(int i=1;i<=n*n;++i) pl[i]=inf; //初始化为inf
for(int i=1;i<=p;++i)
scanf("%d",&a[i]);
for(int i=1;i<=q;++i)
{
scanf("%d",&b[i]);
pl[b[i]]=i; //记录数字b[i]出现的位置
}
for(int i=0;i<=p;++i) dp[i]=0;
--p; --q;
for(int i=2;i<=p;++i)
{
if(pl[a[i]]==inf) continue; //说明a[i]没有在b[i]中出现,继续下一次循环
dp[pl[a[i]]]=dp[pl[a[i]]-1]+1; //跟最长公共子序列是一样的道理
for(int j=pl[a[i]]+1;j<=q;++j) //从pl[a[i]]+1开始,更新dp数组
dp[j]=max(dp[j],dp[j-1]);
}
printf("Case %d: %d\n",++kase,dp[q]+2); //+2是因为两个串首尾都有1,9
}
return 0;
}
附:本题的优化是可以用到其他最长公共子序列问题的