3 solutions
-
1
编辑距离问题,Java代码如下:
import java.util.*; public class Main { static Set<Character> GROUP1 = new HashSet<>(Arrays.asList('w', 'i', 'r', 'e', 'l', '@', 'c', 'o', 'm')); static Set<Character> GROUP2 = new HashSet<>(Arrays.asList('h', 'f', 'v', '#', 'g', 'b', 't', 's')); public static void main(String[] args) { Scanner in = new Scanner(System.in); String word1 = in.nextLine(); String word2 = in.nextLine(); int res = 0; // dp[i][j]:word1的前i-1个字符和word2的前j-1个字符相等需要的步骤 int[][] dp = new int[word1.length() + 1][word2.length() + 1]; // 初始化 dp 数组 for (int i = 0; i <= word1.length(); i++) { dp[i][0] = i * 3; } for (int j = 0; j <= word2.length(); j++) { dp[0][j] = j * 3; } // 遍历 for (int i = 1; i <= word1.length(); i++) { for (int j = 1; j <= word2.length(); j++) { char c1 = word1.charAt(i - 1); char c2 = word2.charAt(j - 1); if (c1 == c2) { dp[i][j] = dp[i - 1][j - 1]; } else { // 插入或删除 dp[i][j] = Math.min(dp[i - 1][j], dp[i][j - 1]) + 3; // 处理替换操作 int replaceCost = getReplaceCost(c1, c2); dp[i][j] = Math.min(dp[i][j], dp[i - 1][j - 1] + replaceCost); } } } res = dp[word1.length()][word2.length()]; System.out.println(res); in.close(); } // 获取两个字符替换的成本 static int getReplaceCost(char c1, char c2) { boolean inGroup1C1 = GROUP1.contains(c1); boolean inGroup1C2 = GROUP1.contains(c2); boolean inGroup2C1 = GROUP2.contains(c1); boolean inGroup2C2 = GROUP2.contains(c2); if (inGroup1C1 && inGroup1C2) { return 1; // 同组替换 } else if (inGroup2C1 && inGroup2C2) { return 1; // 同组替换 } else if ((inGroup1C1 && inGroup2C2) || (inGroup2C1 && inGroup1C2)) { return 2; // 不同组替换 } else { return 3; // 其他 } } }
-
0
s = input() t = input() n = len(s) m = len(t) dp = [[0]*(m+1) for _ in range(n+1)] w1 = 'wirel@com' w2 = 'hfv#gbts' def cal(a,b): if a == b: return 0 elif (a in w1 and b in w1) or (a in w2 and b in w2): return 1 elif (a in w1 and b in w2) or (a in w2 and b in w1): return 2 else: return 3 def main(): #dp需要优化 但是加上main()函数可以加快速度 for i in range(n+1): for j in range(m+1): if i==0 and j==0: dp[i][j]=0 elif i==0: dp[i][j]=j*3 elif j==0: dp[i][j]=i*3 else: dp[i][j]=(i+j)*3 for i in range(1,n+1): for j in range(1,m+1): dp[i][j] = min(dp[i][j],dp[i-1][j],dp[i][j-1])+3 dp[i][j] = min(dp[i][j],dp[i-1][j-1]+cal(s[i-1],t[j-1])) print(dp[n][m]) if __name__ == '__main__': main()
-
0
题目大意
给定两个字符串 a 和 b,可以通过删除、插入和替换三种操作将字符串 a 转换为字符串 b。要求计算将字符串 a 转换为字符串 b 的最小代价
思路
该问题类似于编辑距离问题,因此可以考虑使用动态规划来解决。我们定义 dp[i][j] 表示将字符串 a 的前 i 个字符转换为字符串 b 的前 j 个字符的最小代价。
边界情况 当 i 或 j 为 0 时,表示其中一个字符串为空,此时只能通过插入或删除操作将一个字符串转换为另一个。每次插入或删除的代价是 3,因此 dp[i][j] 的值为 (i + j) * 3。
状态转移 状态转移可以分为三种情况:
删除操作:将 a[i] 删除,那么转换成字符串 b 的代价为 dp[i-1][j] + 3。
插入操作:向 a 中插入一个字符 b[j],那么转换代价为 dp[i][j-1] + 3。
替换操作:将 a[i] 替换为 b[j],代价为 dp[i-1][j-1] + cal(a[i], b[j])。这里 cal 函数用于计算字符 a[i] 和 b[j] 的替换代价。
计算替换代价 cal cal(a, b) 是用于计算两个字符替换代价的函数,规则如下: 1.如果两个字符相同,替换代价为 0; 2.如果两个字符都在字符串 s1("wirel@com")中,或者都在字符串 s2("#hfv#gbts")中,则替换代价为 1; 3.如果一个字符在 s1 中,另一个字符在 s2 中,替换代价为 2; 4.如果两个字符都不在上述字符集中,替换代价为 3;
题目分析
本题的目标是计算两个基站名字的相似度,可以通过动态规划方法来解决,类似于编辑距离问题。在编辑距离问题中,允许对字符串进行三种操作:增、删、改,每种操作都有不同的代价。题目要求通过最少的操作将一个基站名字转换为另一个基站名字,并输出最小得分。
具体来说:
- 插入一个字符,代价是 3 分;
- 删除一个字符,代价也是 3 分;
- 替换一个字符,替换的代价根据给定的分组规则不同,可能是 1 分、2 分或 3 分。
解决思路
动态规划是解决此类问题的有效方法。我们可以定义
dp[i][j]
表示将字符串a
的前i
个字符转换为字符串b
的前j
个字符的最小代价。边界条件
- 当
i
为 0 时,表示a
是空字符串,此时只能通过插入操作将b
的前j
个字符依次插入进来,代价为j * 3
。 - 当
j
为 0 时,表示b
是空字符串,此时只能通过删除操作将a
的前i
个字符依次删除,代价为i * 3
。
状态转移方程
对于任意的
dp[i][j]
,可以通过三种操作转移得到:- 删除操作:将
a[i]
删除,代价为dp[i-1][j] + 3
; - 插入操作:向
a
中插入一个字符b[j]
,代价为dp[i][j-1] + 3
; - 替换操作:将
a[i]
替换为b[j]
,代价为dp[i-1][j-1] + cal(a[i], b[j])
,其中cal
是计算替换代价的函数。
替换代价的计算(函数
cal
)- 如果两个字符相同,替换代价为
0
; - 如果两个字符都在组
s1
("wirel@com")中,或者都在组s2
("#hfv#gbts")中,替换代价为1
; - 如果一个字符在组
s1
,另一个字符在组s2
,替换代价为2
; - 如果两个字符都不在任何组内,替换代价为
3
。
动态规划表的初始化
首先,我们将
dp
数组初始化为(i+j)*3
,表示在最坏的情况下,通过删除和插入操作将两个字符串互相转换的代价。动态规划的填表过程
从
dp[1][1]
开始,通过上面三种操作的最小代价来更新dp[i][j]
。对于每个位置(i, j)
,我们根据前面已经计算好的状态,选择代价最小的一种操作来更新。复杂度分析
- 时间复杂度:由于我们使用了双重循环填表,每次操作的时间复杂度是常数级别,因此总的时间复杂度为 O(n * m),其中
n
是字符串a
的长度,m
是字符串b
的长度。 - 空间复杂度:使用了一个大小为
2005 x 2005
的二维数组dp
,因此空间复杂度为 O(n * m)。
代码如下
cpp
#include <bits/stdc++.h> using namespace std; string a, b; int n, m; // dp 表,用于存储从 a 到 b 的最小转换代价 int dp[2005][2005]; // 字符集,用于判断字符之间的替换代价 string s1 = "wirel@com", s2 = "#hfv#gbts"; // 计算字符 a 和 b 之间的替换代价 int cal(char a, char b) { // 如果两个字符相等,代价为 0 if (a == b) return 0; // 如果两个字符都在 s1 中或都在 s2 中,代价为 1 if (s1.find(a) != string::npos && s1.find(b) != string::npos) { return 1; } else if (s2.find(a) != string::npos && s2.find(b) != string::npos) { return 1; } // 如果一个字符在 s1 中,另一个字符在 s2 中,代价为 2 else if (s2.find(a) != string::npos && s1.find(b) != string::npos) { return 2; } else if (s1.find(a) != string::npos && s2.find(b) != string::npos) { return 2; } // 其他情况下,代价为 3 else { return 3; } } int main() { cin >> a >> b; n = a.length(); m = b.length(); a = ' ' + a; b = ' ' + b; // 初始化 dp 数组 for (int i = 0; i <= n; i++) { for (int j = 0; j <= m; j++) { // 如果 i 和 j 都为 0,即两个空字符串之间的代价为 0 if (!i && !j) dp[i][j] = 0; // 如果 i 为 0,表示将空字符串变为 b 的前 j 个字符,代价为 j * 3(即插入 j 个字符) else if (!i) dp[i][j] = j * 3; // 如果 j 为 0,表示将 a 的前 i 个字符变为空字符串,代价为 i * 3(即删除 i 个字符) else if (!j) dp[i][j] = i * 3; // 否则初始化为 (i+j) * 3 else dp[i][j] = (i + j) * 3; } } // 动态规划过程 for (int i = 1; i <= n; i++) { for (int j = 1; j <= m; j++) { // 计算 dp[i][j] 时,首先考虑插入和删除操作,选择三种操作中的最小值并加上代价 3 dp[i][j] = min({dp[i - 1][j], dp[i][j - 1], dp[i][j]}) + 3; // 然后考虑替换操作,使用 cal 函数计算替换代价 dp[i][j] = min(dp[i][j], dp[i - 1][j - 1] + cal(a[i], b[j])); } } // 输出将字符串 a 转换为字符串 b 的最小代价 cout << dp[n][m]; }
python
# 字符集,用于判断字符之间的替换代价 s1 = "wirel@com" s2 = "#hfv#gbts" # 计算字符 a 和 b 之间的替换代价 def cal(a, b): # 如果两个字符相等,代价为 0 if a == b: return 0 # 如果两个字符都在 s1 中或都在 s2 中,代价为 1 if (a in s1 and b in s1) or (a in s2 and b in s2): return 1 # 如果一个字符在 s1 中,另一个字符在 s2 中,代价为 2 elif (a in s1 and b in s2) or (a in s2 and b in s1): return 2 # 其他情况下,代价为 3 else: return 3 def main(): # 输入两个字符串 a = input().strip() b = input().strip() # 获取字符串的长度 n = len(a) m = len(b) # 初始化 dp 数组,大小为 (n+1) x (m+1) dp = [[0] * (m + 1) for _ in range(n + 1)] # 初始化 dp 数组 for i in range(n + 1): for j in range(m + 1): # 如果 i 和 j 都为 0,即两个空字符串之间的代价为 0 if i == 0 and j == 0: dp[i][j] = 0 # 如果 i 为 0,表示将空字符串变为 b 的前 j 个字符,代价为 j * 3(即插入 j 个字符) elif i == 0: dp[i][j] = j * 3 # 如果 j 为 0,表示将 a 的前 i 个字符变为空字符串,代价为 i * 3(即删除 i 个字符) elif j == 0: dp[i][j] = i * 3 # 否则初始化为 (i+j) * 3 else: dp[i][j] = (i + j) * 3 # 动态规划过程 for i in range(1, n + 1): for j in range(1, m + 1): # 计算 dp[i][j] 时,首先考虑插入和删除操作,选择三种操作中的最小值并加上代价 3 dp[i][j] = min(dp[i - 1][j], dp[i][j - 1], dp[i][j]) + 3 # 然后考虑替换操作,使用 cal 函数计算替换代价 dp[i][j] = min(dp[i][j], dp[i - 1][j - 1] + cal(a[i - 1], b[j - 1])) # 输出将字符串 a 转换为字符串 b 的最小代价 print(dp[n][m]) if __name__ == "__main__": main()
java
import java.util.Scanner; public class Main { static String s1 = "wirel@com"; // 用于判断字符替换代价的字符集 s1 static String s2 = "#hfv#gbts"; // 用于判断字符替换代价的字符集 s2 static int[][] dp = new int[2005][2005]; // dp 数组,存储最小转换代价 // 计算字符 a 和 b 之间的替换代价 public static int cal(char a, char b) { // 如果两个字符相等,代价为 0 if (a == b) return 0; // 如果两个字符都在 s1 中或都在 s2 中,代价为 1 if (s1.indexOf(a) != -1 && s1.indexOf(b) != -1) { return 1; } else if (s2.indexOf(a) != -1 && s2.indexOf(b) != -1) { return 1; } // 如果一个字符在 s1 中,另一个字符在 s2 中,代价为 2 else if (s2.indexOf(a) != -1 && s1.indexOf(b) != -1) { return 2; } else if (s1.indexOf(a) != -1 && s2.indexOf(b) != -1) { return 2; } // 其他情况下,代价为 3 else { return 3; } } public static void main(String[] args) { Scanner sc = new Scanner(System.in); // 输入两个字符串 String a = sc.nextLine(); String b = sc.nextLine(); int n = a.length(); int m = b.length(); // 初始化 dp 数组 for (int i = 0; i <= n; i++) { for (int j = 0; j <= m; j++) { // 如果 i 和 j 都为 0,即两个空字符串之间的代价为 0 if (i == 0 && j == 0) { dp[i][j] = 0; } // 如果 i 为 0,表示将空字符串变为 b 的前 j 个字符,代价为 j * 3(即插入 j 个字符) else if (i == 0) { dp[i][j] = j * 3; } // 如果 j 为 0,表示将 a 的前 i 个字符变为空字符串,代价为 i * 3(即删除 i 个字符) else if (j == 0) { dp[i][j] = i * 3; } // 否则初始化为 (i+j) * 3 else { dp[i][j] = (i + j) * 3; } } } // 动态规划过程 for (int i = 1; i <= n; i++) { for (int j = 1; j <= m; j++) { // 计算 dp[i][j] 时,首先考虑插入和删除操作,选择三种操作中的最小值并加上代价 3 dp[i][j] = Math.min(Math.min(dp[i - 1][j], dp[i][j - 1]), dp[i][j]) + 3; // 然后考虑替换操作,使用 cal 函数计算替换代价 dp[i][j] = Math.min(dp[i][j], dp[i - 1][j - 1] + cal(a.charAt(i - 1), b.charAt(j - 1))); } } // 输出将字符串 a 转换为字符串 b 的最小代价 System.out.println(dp[n][m]); } }
- 1
Information
- ID
- 139
- Time
- 1000ms
- Memory
- 256MiB
- Difficulty
- 5
- Tags
- # Submissions
- 524
- Accepted
- 59
- Uploaded By