題意:有兩個字符串,從兩個字符串中取字符組成一個新串,並規定跨度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,結果就過了。