可以发现最后一个字母一定是最左最有,那么dp方程很好转移,但是是3方的,如果先减1话,我们想到每次扫到的结果可以重复利用,于是每次维护当前数的第一个位置在哪即可。
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#include<cmath>
using namespace std;
const int maxn=5005;
const int mod=1000000007;
int dp[maxn][maxn];
int dp1[maxn][maxn];
int a[maxn];
int ne[maxn];
int pre[maxn];
int l1[maxn],r1[maxn];
int b[maxn];
int main()
{
int n;
while (scanf("%d",&n)!=EOF) {
for(int i=1;i<=n;i++){ scanf("%d",&a[i]);
b[i]=a[i];
}
sort(b+1,b+1+n);
for(int i=1;i<=n;i++) a[i]=lower_bound(b+1,b+1+n,a[i])-b;
memset(l1,0,sizeof(l1));
memset(r1,0,sizeof(r1));
for(int i=1;i<=n;i++){
r1[a[i]]=i;
}
memset(ne,0,sizeof(ne));
for(int i=n;i>=1;i--){
l1[a[i]]=i;
}
for(int i=n;i>=1;i--){
dp[i][i]=1;
dp1[i][i]=1;
int ma=0,su=0;
for(int j=i+1;j<=n;j++){
dp[i][j]=0;
if(a[i]==a[j]){
dp[i][j]=ma+2;
if(ma==0) dp1[i][j]=1;
else dp1[i][j]=su;
}
if(a[j]<=a[i]){
if(ne[a[j]]==0) ne[a[j]]=j;
if(ma<dp[ne[a[j]]][j]){
ma=dp[ne[a[j]]][j];
su=dp1[ne[a[j]]][j];
}
else if(ma==dp[ne[a[j]]][j]){
if(pre[a[j]]!=0){
if(dp[ne[a[j]]][pre[a[j]]]==dp[ne[a[j]]][j]){
su-=dp1[ne[a[j]]][pre[a[j]]];
su%=mod;
su+=dp1[ne[a[j]]][j];
su%=mod;
}
else{
su+=dp1[ne[a[j]]][j];
su%=mod;
}
}
else{
su+=dp1[ne[a[j]]][j];
su%=mod;
}
}
pre[a[j]]=j;
}
}
for(int j=i+1;j<=n;j++){
ne[a[j]]=0;
pre[a[j]]=0;
}
}
int s=0;
for(int i=1;i<=n;i++){
if(l1[i]!=0){
s=max(s,dp[l1[i]][r1[i]]);
}
}
int ans=0;
for(int i=1;i<=n;i++){
if(l1[i]!=0){
if(s==dp[l1[i]][r1[i]]){
ans+=dp1[l1[i]][r1[i]];
ans%=mod;
}
}
}
ans=(ans+mod)%mod;
printf("%d %d\n",s,ans);
}
}