Description
给出两个字符串$a$,$b$,将他们穿插起来(相对位置不变),要求最小化$sum L(c)$,其中$L(c)$的定义时在穿插完的字符串中字符$c$的最大位置与最小位置的差。
$n leq 5000$。
Solution
问题的转化
这样的问题并不好用子问题来做,考虑最小化贡献。
贡献是可以$DP$的。令$dp_{i,j}$表示将$a$的前$i$个与$b$的前$j$个合并后对答案的最优贡献。维护每个字母第一次出现的状态和最后一次出现的状态转移即可。
/*By DennyQi 2019*/
#include <cstdio>
#include <queue>
#include <cstring>
#include <algorithm>
using namespace std;
typedef long long ll;
const int MAXN = 10010;
const int MAXM = 20010;
const int INF = 0x3f3f3f3f;
inline int Max(const int a, const int b){ return (a > b) ? a : b; }
inline int Min(const int a, const int b){ return (a < b) ? a : b; }
inline int read(){
int x = 0; int w = 1; register char c = getchar();
for(; c ^ '-' && (c < '0' || c > '9'); c = getchar());
if(c == '-') w = -1, c = getchar();
for(; c >= '0' && c <= '9'; c = getchar()) x = (x<<3) + (x<<1) + c - '0'; return x * w;
}
int T,dp[5010],cnt[5010],lsta[30],lstb[30],fsta[30],fstb[30],la,lb;
char a[5010],b[5010];
inline void Init_cnt(){
memset(lsta,0,sizeof(lsta));
memset(lstb,0,sizeof(lstb));
memset(fsta,0x3f,sizeof(fsta));
memset(fstb,0x3f,sizeof(fstb));
for(int i = 1; i <= la; ++i){
lsta[a[i]-'A'] = max(lsta[a[i]-'A'],i);
fsta[a[i]-'A'] = min(fsta[a[i]-'A'],i);
}
for(int i = 1; i <= lb; ++i){
lstb[b[i]-'A'] = max(lstb[b[i]-'A'],i);
fstb[b[i]-'A'] = min(fstb[b[i]-'A'],i);
}
}
inline void DP(){
memset(dp,INF,sizeof(dp));
memset(cnt,0,sizeof(cnt));
char cur;
dp[0] = 0;
for(int i = 0; i <= la; ++i){
for(int j = 0; j <= lb; ++j){
if(i==0 && j==0) continue;
if(i == 0){
cnt[j] = cnt[j-1];
cur = b[j]-'A';
if(j == fstb[cur]) ++cnt[j];
if(j == lstb[cur] && lsta[cur] == 0) --cnt[j];
}
else{
cur = a[i]-'A';
if(j >= lstb[cur] && i >= lsta[cur]) --cnt[j];
if(j < fstb[cur] && i == fsta[cur]) ++cnt[j];
}
dp[j] = min((j==0)?INF:dp[j-1], (i==0)?INF:dp[j])+cnt[j];
}
}
}
int main(){
scanf("%d",&T);
while(T--){
scanf("%s%s",a+1,b+1);
la = strlen(a+1), lb = strlen(b+1);
Init_cnt();
DP();
printf("%d
",dp[lb]);
}
return 0;
}