UVALive 6697 - Homework Evaluation(dp,字符串匹配得分)

题目链接:
UVALive 6697 - Homework Evaluation
题意:
给出两个字符串,用第二个去匹配第一个字符串,第二个字符串中的字母可以看成:新插入的,有丢失的,正确匹配的,不正确的。

  • 正确的+8分
  • 错误的-5分
  • 插入或删除一个字母-3分
  • 连续插入或删除额外扣除-4分

例如:
Whatagreatisl/andtovisit            gr//tisllamd/
应用上面的规则可以得到的分数是:
8851334264598=42
现在给出两个字符串(第一个长度不大于100,第二个长度不大于50),问最优得分是多少?

分析:
dp
我们可以发现第二个字符串的每个字母要么和第一个字符串的某个字母匹配,要么看成是插在第一个字符串的某个字母前面。

  • dp[0][i][j] 表示第二个字符串的第 i 个字母和第一个字符串的第j个字母匹配时,前 i 个字母的得分
  • dp[1][i][j]表示第二个字符串的第 i 个字母插在第一个字符串的第j个字母前面时,前 i 个字母的得分

考虑状态转移方程。设字符串的下标都是从1开始,第一个字符串为s[],第二个字符串为 ss[]

  • 考虑初始化。
memset(dp, 0, sizeof(dp));

if( i > 1) dp[0][i][1] = -3 * (i - 1) - 4;
if(ss[i] == s[1]) dp[0][i][1] += 8;
else dp[0][i][1] -= 5;

dp[1][i][1] = -i * 3 - 4;
dp[0][i][0] = dp[1][i][0] = -inf; 
  • 当第二个字符串的第 i 和第一个字符串的第j个匹配时,遍历第一个字符串的前 j 个字母,考虑第二个字符串的第i1个字母的匹配位置 k ,先初始化dp[0][i][j]=inf
    对于 dp[0][i][j] ,也就是恰好 ij
    • 如果 k<j1 ,如果第 i1 个也和第 k 个恰好匹配(从dp[0][i1][k]转移),那么就相当于在第 i 个和第i1个之间连续删除了 (jk1) ,所以
      dp[0][i][j]=max(dp[0][i][j],dp[0][i1][k]3(jk1)4);
      如果第 i1 个插入在第 k 个之前(从dp[1][i1][k]转移),那么就相当于在第 i 个和第i1个之间连续删除了 jk ,所以有:
      dp[0][i][j]=max(dp[0][i][j],dp[1][i1][k]3(jk)4);
    • 如果 k=j1 ,同样的道理可得:
      dp[0][i][j]=max(dp[0][i][j],dp[0][i1][k]);//
      dp[0][i][j]=max(dp[0][i][j],dp[1][i1][k]3(jk)4);//jk=1,1

      对于 dp[1][i][j] ,也就是恰好 ij 之前。同样的道理考虑状态转移。
      对于第 i1 个和第 j 个匹配或者插入在第j个之前的情况也是需要考虑的。
dp[0][i][j] = max(dp[0][i][j], dp[1][i - 1][j]);
if(ss[i] == s[j]) dp[0][i][j] += 8;
else dp[0][i][j] -= 5;

if(i > 1) dp[1][i][j] = max(dp[1][i][j], dp[1][i - 1][j] - 3);

最后扫一下第二个字符串的最后一个字母在第一个字符串中的匹配位置,结果取最右。别忘了第二字符串的最后一个字母可以看成插入在第一个字符串的最后一个字母后面的情况。

#include <iostream>
#include <cstdio>
#include <cstring>
#include <string>
#include <algorithm>
#include <climits>
#include <cmath>
#include <ctime>
#include <cassert>
#define IOS ios_base::sync_with_stdio(0); cin.tie(0);
using namespace std;
typedef long long ll;
const int MAX_N = 110;

int T;
char s[MAX_N], ss[MAX_N];
int dp[2][MAX_N][MAX_N];

int main()
{
    scanf("%d", &T);
    while(T--) {
        scanf("%s%s", s + 1, ss + 1);
        int len1 = strlen(s + 1) + 1, len2 = strlen(ss + 1) + 1;
        if(len1 == len2 && len1 == 2) {
            if(s[1] == ss[1]) printf("8\n");
            else printf("-5\n");
            continue;
        }
        memset(dp, 0, sizeof(dp));
        for(int i = 1; i < len2; ++i) {
            if( i > 1) dp[0][i][1] = -3 * (i - 1) - 4;
            if(ss[i] == s[1]) dp[0][i][1] += 8;
            else dp[0][i][1] -= 5;

            dp[1][i][1] = -i * 3 - 4;
            dp[0][i][0] = dp[1][i][0] = INT_MIN / 2;
            for(int j = 2; j < len1 + 1; ++j) {
                dp[0][i][j] = dp[1][i][j] = INT_MIN;

                for(int k = 0; k < j; ++k) {
                    if(k < j - 1) dp[0][i][j] = max(dp[0][i][j], dp[0][i - 1][k] - 3 * (j - k - 1) - 4);
                    else dp[0][i][j] = max(dp[0][i][j], dp[0][i - 1][k]);
                    dp[0][i][j] = max(dp[0][i][j], dp[1][i - 1][k] - 3 * (j - k) - 4);

                    if(k < j - 1) dp[1][i][j] = max(dp[1][i][j], dp[0][i - 1][k] - 3 * (j - k - 1) - 11);
                    else dp[1][i][j] = max(dp[1][i][j], dp[0][i - 1][k] - 7);
                    dp[1][i][j] = max(dp[1][i][j], dp[1][i - 1][k] - 3 * (j - k) - 11);
                }
                dp[0][i][j] = max(dp[0][i][j], dp[1][i - 1][j]);
                if(ss[i] == s[j]) dp[0][i][j] += 8;
                else dp[0][i][j] -= 5;
                if(i > 1) dp[1][i][j] = max(dp[1][i][j], dp[1][i - 1][j] - 3);
            }
        }

        int ans = INT_MIN, n = len2 - 1;
        for(int i = 1; i < len1; ++i) {
            ans = max(ans, dp[0][n][i]);
            ans = max(ans, dp[1][n][i]);
        }
        ans = max(ans, dp[1][n][len1]);
        printf("%d\n", ans);
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值