UVa 1625

题目大意输入两个序列,将两个序列合并成一个序列,要求原有的顺序不能改变。

思路

dp

首先要想到如果按题目要求合并序列可以每次把一个序列的开头放在合成序列的尾部,此时可以设计状态d(i,j) 表示当前第一个序列到

了第i个位置,第二个序列到了第j个位置的最小花费,最后的结果就是d(n,m) ,n是第一个序列的长度,m是第二个序列的长度。状态

转移方程为d(i+1, j) = min{d(i+1,j), d(i,j)+w(i+1,j)} 和 d(i, j+1) = min{d(i,j+1), d(i,j)+w(i,j+1)},和LCS问题类似。其中w(i+1,j)表示移动第

i+1转移所需要的代价,对当前所有的在序列中的字母进行判断,如果出现了该字母但还有字母没有放在新序列,则说明代价要加1。

通过预处理可以实现O(1)的判断。

#include <bits/stdc++.h>
using namespace std;
const int maxn = 5000 + 10;
const int INF = 1000000007;
char s[maxn], t[maxn];

int d[maxn][maxn];

pair<int,int> st[30], tt[30];
int n, m;
void make_table(pair<int, int> * table, char * a, int len){
    for(int i = 1; i <= len; ++i){
        int id = a[i] - 'A';
        table[id].second = i;
    }
    for(int i = len; i >= 1; --i){
        int id = a[i] - 'A';
        table[id].first = i;
    }
}

void init(){
    for(int i = 0; i < 30; ++i){
        st[i].first = tt[i].first = INF;
        st[i].second = tt[i].second = -1;
    }
    for(int i = 0; i <= n + 1; ++i)
        for(int j = 0; j <= m + 1; ++j)
            d[i][j] = INF;
}

inline int w(int i, int j){
    int sum = 0;
    for(int k = 0; k < 26; ++k)
        if((st[k].first <= i || tt[k].first <= j) &&
           (st[k].second > i || tt[k].second > j))
            sum++;
    return sum;
}

int solve(){
    n = strlen(s + 1), m = strlen(t + 1);
    init();
    make_table(st, s, n);
    make_table(tt, t, m);
    d[0][0] = 0;
    for(int i = 0; i <= n; ++i)
        for(int j = 0; j <= m; ++j){
            if(i + 1 <= n) d[i + 1][j] = min(d[i + 1][j], d[i][j] + w(i + 1, j));
            if(j + 1 <= m) d[i][j + 1] = min(d[i][j + 1], d[i][j] + w(i, j + 1));
        }
    return d[n][m];
}


int main()
{
    int T; scanf("%d", &T);
    while(T --){
        scanf("%s%s", s + 1, t + 1);
        printf("%d\n", solve());
    }
    return 0;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值