题意:有两个字符串,从两个字符串中取字符组成一个新串,并规定跨度L(c)表示该字符在新串中最大位置和最小位置的差,最终求所有L(c)的和;
解:这个题的思路好想,就是用dp[i][j]来表示当前已经从第一个中取了i个从第二个中取了j个字符后的最小跨度,但麻烦的就是怎么实现这个状态,每次都重新算一遍每个的跨度时间肯定承受不了,所以有个简便的方法就是求当前已经有多少字符已经被取了但是最后一个该字符还没有被取到,因为只要有一个字符没有结束被取,那么每取一个字符该字符的跨度都要+1,所以当前没有结束的字符的个数就应该是取一个字符后总跨度应该加的,这里可以用一个c数组表示,然后每次都去更新这个数组的状态就好了;
代码:
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int maxed=5000+10;
int dp[maxed][maxed],sta1[27],sta2[27],en1[27],en2[27],c[maxed][maxed];
char s1[maxed],s2[maxed];
int main()
{
int n;
scanf("%d",&n);
while(n--){
scanf("%s",s1+1);
scanf("%s",s2+1);
memset(sta1,0x3f3f3f3f,sizeof(sta1));
memset(sta2,0x3f3f3f3f,sizeof(sta2));
memset(en1,0,sizeof(en1));
memset(en2,0,sizeof(en2));
int len1=strlen(s1+1),len2=strlen(s2+1);
for(int i=1;i<=len1;i++){
sta1[s1[i]-'A']=min(sta1[s1[i]-'A'],i);
en1[s1[i]-'A']=max(en1[s1[i]-'A'],i);
}
for(int i=1;i<=len2;i++){
sta2[s2[i]-'A']=min(sta2[s2[i]-'A'],i);
en2[s2[i]-'A']=max(en2[s2[i]-'A'],i);
}
for(int i=0;i<=len1;i++)
for(int j=0;j<=len2;j++){
if(!i&&!j)
continue;
int m1=1<<30,m2=1<<30;
if(i)
m1=dp[i-1][j]+c[i-1][j];
if(j)
m2=dp[i][j-1]+c[i][j-1];
dp[i][j]=min(m1,m2);
//cout<<"sdasdasd"<<dp[i][j]<<endl;
if(dp[i][j]==m1){ //这里就是去每次都更新一下c数组
c[i][j]=c[i-1][j];
if(sta1[s1[i]-'A']==i&&sta2[s1[i]-'A']>j)
c[i][j]++;
if(en1[s1[i]-'A']==i&&en2[s1[i]-'A']<=j)
c[i][j]--;
}
else if(dp[i][j]==m2){
c[i][j]=c[i][j-1];
if(sta2[s2[j]-'A']==j&&sta1[s2[j]-'A']>i)
c[i][j]++;
if(en2[s2[j]-'A']==j&&en1[s2[j]-'A']<=i)
c[i][j]--;
}
}
printf("%d\n",dp[len1][len2]);
}
return 0;
}
这个代码感觉时间卡的刚刚好,因为我第一次多加了两个memset,就超时了,因为之前也因为这个超时过,所以删掉了两个memset,结果就过了。