📅  最后修改于: 2023-12-03 15:42:12.821000             🧑  作者: Mango
给定两个字符串S1和S2,S1的长度为n,S2的长度为m。将S1插入到S2中以形成新的字符串。让我们定义一个字符串得分为:两个相邻字符的距离乘以它们的ASCII码差的绝对值。插入S1后,求得最佳得分。
请编写一个函数 int bestScore(string S1, string S2)
以实现上述逻辑。函数输入输出如下:
S1
:一个长度为n的字符串,表示要插入到S2中的字符串。($1 \leq n \leq 2000$)S2
:一个长度为m的字符串,表示要插入字符串的目标字符串。($1 \leq m \leq 10^5$)输入:
S1 = "he"
S2 = "talker"
输出:
219
解释:
将"he"插入"S2"的第2个字符之后得到"Mtalker",其得分为
(1)*(77-109) + (1)*(101-97) + (2)*(97-108) + (1)*(108-101)
= -988 + 4 - 22 + 7 = -999
将"he"插入"S2"的第3个字符之后得到"tHalker",其得分为
(1)*(84-116) + (1)*(104-72) + (2)*(97-108) + (1)*(108-101)
= -988 + 32 + 22 + 7 = -927
将"he"插入"S2"的第4个字符之后得到"taHlker",其得分为
(1)*(84-116) + (2)*(104-97) + (3)*(108-72) + (1)*(101-108)
= -988 + 14 - 108 + 7 = -1075
...
得分最大为219
本题是一道动态规划(DP)的经典问题。我们可以将插入新字符串的操作按照顺序进行,从S2的每一个字母开始尝试插入,计算出插入每个位置后可能所得的最佳结果,并记录这个最佳结果。得到了所有位置的最佳结果后,可以在这些结果中选择最佳的,并返回它的值。
因为最优步骤一定是按照顺序插入新字符串,所以我们可以用一个二维数组 dp[i][j]
来记录插入S1中前i个字符到S2中前j个字符最大的得分。因为得分是由“两个相邻字符的距离乘以它们的ASCII码差的绝对值”计算而来的,所以可以用定义数组 score[i][j]
来保存两个字符之间距离的权重。对于某个位置 dp[i][j]
,如果将S1中第i个字符插入到S2中第j个位置上,总得分为:上一个得分加上插入得分减去两个子串之间得分。
$$dp[i][j] = \max_{1 \leq k \leq j} dp[i-1][k] + \text{插入得分} - score[k][j-1]$$
因此,我们可以遍历所有的 i
和 j
,每次计算出上式的最大值即可。插入得分可以表示为 $\text{插入权重}\times\left|s_2\left(j\right)-s_1\left(i\right)\right|$。
$$\text{插入得分} = \text{插入权重} \times \left| \text{ASCII}(s_2(j)) - \text{ASCII}(s_1(i)) \right|$$
通过计算各个位置的得分,我们可以得出最佳的得分。
#include <bits/stdc++.h>
using namespace std;
int bestScore(string S1, string S2) {
int n = S1.length(), m = S2.length();
vector<vector<int>> dp(n+1, vector<int>(m+1));
vector<vector<int>> score(128, vector<int>(128));
for(int i=0; i<128; i++){
for(int j=0; j<128; j++){
score[i][j] = abs(i-j);
}
}
int res = 0;
for(int i=1; i<=n; i++){
for(int j=1; j<=m; j++){
dp[i][j] = -0x3f3f3f3f;
for(int k=j-1; k>=0; k--){
dp[i][j] = max(dp[i][j], dp[i-1][k] + score[S1[i-1]][S2[j-1]] - score[S2[k]][S2[j-1]]);
res = max(res, dp[i][j]);
}
}
}
return res;
}