Color Length
(problem:)
给出两个颜色序列,长度分别为N,M,现在要将它们合并成一个新序列,每次可以将把一个序列的开头颜色放入新序列的尾部
记st(c)表示新序列中颜色c第一次出现的位置,ed(c)表示新序列中颜色c最后一次出现的位置
记L(c)=ed(c)-st(c),现在要最小化(sum_{c}L(c))
(data) (range:)
(N,M<=5*10^3)
(solution:)
太菜了,看了题解才明白神仙做法
考虑到这题是一个dp
根据套路记(dp_{i,j})表示第一个序列已经将i个元素加入新序列,第二个序列已经将j个元素加入新序列时的最小代价
然后发现,,怎么转移??!!
那么我们再记(cost_{i,j})表示此时的还没有结束的颜色的数量(就是这个颜色不是第一个也不是最后一个)
那么就有转移方程:
(dp_{i,j}=min(dp_{i-1,j}+cost_{i-1,j},dp_{i,j-1}+cost_{i,j-1}))
最后一步:如何求出(cost_{i,j})?
可以预处理每个颜色分别在两个序列(分别设为a,b)中第一次和最后一次出现的位置
我们考虑(cost_{i,j-1}->cost_{i,j}(cost_{i-1,j}->cost_{i,j}同理))
如果b_j是这个颜色在b中第一次出现的时候,倘若此时a还没有加入这个颜色,那么(cost_{i,j}=cost_{i,j-1}+1)
如果b_j是这个颜色在b中最后一次出现的时候,倘若此时a已经加完了这种颜色,那么(cost_{i,j}=cost_{i,j-1}-1)
(space) (time) (complexity:)
(space)&(time:) (O(n*m))
(note:)
数组初始化又写炸了啊啊啊
(code:)
#include<bits/stdc++.h>
using namespace std;
const int N=5e3+5,Z=30;
int t,lena,lenb,sta[Z],eda[Z],stb[Z],edb[Z];
int dp[N][N],cost[N][N];
char a[N],b[N];
inline void pre()
{
for(int i=0;i<=lena;++i)
for(int j=0;j<=lenb;++j)
dp[i][j]=1e9,cost[i][j]=0;
fill(sta,sta+Z,lena+1),fill(eda,eda+Z,-1),
fill(stb,stb+Z,lenb+1),fill(edb,edb+Z,-1);
}
int main()
{
scanf("%d",&t);
while(t--)
{
scanf("%s%s",a+1,b+1);
lena=strlen(a+1),lenb=strlen(b+1);
pre();
for(char ch='A';ch<='Z';++ch)
{
for(int i=1;i<=lena;++i)
if(a[i]==ch){sta[ch-'A']=i;break;}
for(int i=lena;i>=1;--i)
if(a[i]==ch){eda[ch-'A']=i;break;}
for(int i=1;i<=lenb;++i)
if(b[i]==ch){stb[ch-'A']=i;break;}
for(int i=lenb;i>=1;--i)
if(b[i]==ch){edb[ch-'A']=i;break;}
}
dp[0][0]=cost[0][0]=0;
for(int i=0;i<=lena;++i)
for(int j=0;j<=lenb;++j)
{
if(!i&&!j)continue;
int c1=a[i]-'A',c2=b[j]-'A';
if(i)
{
cost[i][j]=cost[i-1][j];
dp[i][j]=min(dp[i-1][j]+cost[i-1][j],dp[i][j]);
if(i==sta[c1]&&j<stb[c1])++cost[i][j];
if(i==eda[c1]&&j>=edb[c1])--cost[i][j];
}
if(j)
{
cost[i][j]=cost[i][j-1];
dp[i][j]=min(dp[i][j-1]+cost[i][j-1],dp[i][j]);
if(i<sta[c2]&&j==stb[c2])++cost[i][j];
if(i>=eda[c2]&&j==edb[c2])--cost[i][j];
}
}
printf("%d
",dp[lena][lenb]);
}
return 0;
}