当前位置: 代码迷 >> 综合 >> UVALive 6697 - Homework Evaluation(dp,字符串匹配得分)
  详细解决方案

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

热度:62   发布时间:2023-12-08 10:34:36.0

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

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

例如:
Whatagreatisl/andtovisit            gr//tisllamd/
应用上面的规则可以得到的分数是:
8?85?13?34?264?5?9?8=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 个字母,考虑第二个字符串的第 i?1 个字母的匹配位置 k ,先初始化 dp[0][i][j]=?inf
    对于 dp[0][i][j] ,也就是恰好 ij
    • 如果 k<j?1 ,如果第 i?1 个也和第 k 个恰好匹配(从 dp[0][i?1][k] 转移),那么就相当于在第 i 个和第 i?1 个之间连续删除了 (j?k?1) ,所以
      dp[0][i][j]=max(dp[0][i][j],dp[0][i?1][k]?3?(j?k?1)?4);
      如果第 i?1 个插入在第 k 个之前(从 dp[1][i?1][k] 转移),那么就相当于在第 i 个和第 i?1 个之间连续删除了 j?k ,所以有:
      dp[0][i][j]=max(dp[0][i][j],dp[1][i?1][k]?3?(j?k)?4);
    • 如果 k=j?1 ,同样的道理可得:
      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);//j?k=1,1

      对于 dp[1][i][j] ,也就是恰好 ij 之前。同样的道理考虑状态转移。
      对于第 i?1 个和第 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;
}