Problem Description
输入两个颜色序列,要求按顺序合并成一个序列,即每次把一个序列的开头的颜色放到新序列的尾部。对于每个颜色c来说,其跨度l(c),表示c的最大位置和最小位置的差。
你的任务是找一种合并方式使得所有l(c)的总和最小。dp[i][j]表示第一个串移走了i个元素,第二个串移走了j个元素还需要多少费用。
dp[i][j]表示的是,当序列1取了i个,序列2取了j个时,对“已经出现,但还没有结束的颜色”的影响
普通的状态转移比较麻烦,要考虑每个颜色上一次出现的位置。我们换一种思路,不是等到一个颜色转移之后再计算,而是每一次累加,当把一个颜色移到最终序列前,把所有已经出现但是没有结束的颜色的l(c)加一。进一步,不需要关注每个颜色的l(c),只需要知道多少种颜色已经开始但是没有结束。
#include<iostream>
#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<cmath>
#include<vector>
#include<stack>
#include<set>
#include<map>
#include<queue>
#include<algorithm>
using namespace std;
char a[5005],b[5005];
int dp[5005][5005];
int starta[26],enda[26],startb[26],endb[26];
int main(){
int kase;
scanf("%d",&kase);
int lena,lenb;
int i,j,k;
while(kase--){
scanf("%s",a);
scanf("%s",b);
lena=strlen(a);
lenb=strlen(b);
for(i=0;i<26;i++){
starta[i]=99999999;
startb[i]=99999999;
enda[i]=-99999999;
endb[i]=-99999999;
}
for(i=0;i<lena;i++){
a[i]-='A';
if(starta[a[i]]==99999999){
starta[a[i]]=i+1;
}
enda[a[i]]=i+1;
}
for(i=0;i<lenb;i++){
b[i]-='A';
if(startb[b[i]]==99999999){
startb[b[i]]=i+1;
}
endb[b[i]]=i+1;
}
for(i=0;i<=lena;i++){
for(j=0;j<=lenb;j++){
dp[i][j]=0;
for(k=0;k<26;k++){
if((i>=starta[k]||j>=startb[k])&&(i<enda[k]||j<endb[k]))
dp[i][j]++;
}
if(i==0&&j==0)
continue;
else if(i==0)
dp[i][j]+=dp[i][j-1];
else if(j==0)
dp[i][j]+=dp[i-1][j];
else
dp[i][j]+=min(dp[i-1][j],dp[i][j-1]);
}
}
printf("%d\n",dp[lena][lenb]);
}
return 0;
}