给定两个单词 word1 和 word2,找到使得 word1 和 word2 相同所需的最小步数,每步可以删除任意一个字符串中的一个字符。
示例 1:
输入: "sea", "eat" 输出: 2 解释: 第一步将"sea"变为"ea",第二步将"eat"变为"ea"
说明:
- 给定单词的长度不超过500。
- 给定单词中的字符只含有小写字母。
解题思路:
动态规划。
首先需要明白这两个容易混淆的概念,我也曾多次分不清。
- 子串。字符串的子串必需是原字符串中一段连续的片断,子串必然是子序列。
- 子序列。子序列是字符串的子集,是原字符串删除一定的字符得到,子序列未必是子串。
仔细思考,本题是求两个字符串的最长子序列len,最终返回size1+size2-2*len。既然是动态规划,必然要找到其中的递推关系,假设pos1,pos2分别是word1的子串[pos1,size1),word2的子串[pos2,size2)的初始位置,dp[pos1][pos2]则代表这两个子串的最长子序列。这个定义最初可能有些难懂,还希望能仔细体会。于是有下面的递推关系,主要有:
- if word1[pos1]==word2[pos2] ,dp[pos1][pos2] = dp[pos1+1][pos2+1]+1;
- else dp[pos1][pos2] = max (dp[pos1+1][pos2],dp[pos1][pos2+1]);
当然要考虑边界条件,防止数组溢出。
class Solution { public: int minDistance(string word1, string word2) { int size1 = word1.size(), size2 = word2.size(), i,j; if (size1 == 0 || size2 == 0) return size1 + size2; vector<vector<int>> dp(size1, vector<int>(size2, 0)); for (i = size1; i >= 1; i--) { for (j = size2; j >= 1; j--) { if (i == size1&&j == size2) dp[i - 1][j - 1] = word1.back() == word2.back() ? 1 : 0; else if (j == size2) { if (word1[i - 1] == word2[j - 1]) dp[i - 1][j - 1] = 1; else dp[i - 1][j - 1] = dp[i][j - 1]; } else if (i == size1) { if (word1[i - 1] == word2[j - 1]) dp[i - 1][j - 1] = 1; else dp[i - 1][j - 1] = dp[i - 1][j]; } else { if (word1[i - 1] == word2[j - 1]) dp[i - 1][j - 1] = dp[i][j] + 1; else { dp[i - 1][j - 1] = dp[i - 1][j] > dp[i][j - 1] ? dp[i - 1][j] : dp[i][j - 1]; } } } } return size1 + size2 - 2 * dp[0][0]; } }; static int pr = []() { std::ios::sync_with_stdio(false); cin.tie(NULL); return 0; }(); |