原题截图
题意分析
有两个字符串,有三种操作:1. 在字符串的任意位置添加任意一个字符;2. 删除字符串中的任意一个字符;3. 将字符串中的任意一个字符修改为任意的其他字符。问最少需要多少次操作,能将第一个字符串变为第二个字符串。
解题思路
本题是一道经典算法问题:编辑距离(但由于本人动态规划较弱,思考较长时间后,还是上网查询了题解才做出本题)。题目的含义较为简单,即最少多少次增、删、改操作后能将一个字符串变为另一个字符串。经过思考可以发现,本题难以使用简单的贪心算法得出最优解,于是考虑使用动态规划。
使用动态规划解决本题时,可以定义二维数组int dp[][],设第一个字符串为s,第二个字符串为t,dp[i][j]代表由s的前i个字符组成的字符串,最少在经过dp[i][j]次操作后,可以变为由t的前j个字符组成的字符串。然而,转移状态的方法我却思考很长时间都没有想到,在查询题解后,我使用了两层循环,并定义了状态转移方程dp[i][j] = min({dp[i-1][j]+1, dp[i][j-1]+1, dp[i-1][j-1] + (s[i]==t[j] ? 0 : 1)});,即字符串s的前i个字符,可以由
1. 删除第i个字符,然后执行dp[i-1][j]步某种操作;
2. 执行dp[i][j-1]步某种操作,然后在末尾添加字符串t的第j个字符;
3. 对前i-1个字符的部分执行dp[i-1][j-1]步操作,变为t的前j-1个字符,然后将最后一个字符修改为t的第j个字符(如果最后一个字符和t的j个字符相同则无需修改)。
这三种方法之一,变为字符串t的前j个字符,最小操作次数即为上述3种操作所需次数的最小值。 对于初始状态,可以用dp[i][0]代表从字符串s的前i个字符变为空串所需的操作次数,用dp[0][j]代表从空串变为字符串t的前j个字符所需的操作次数,容易得出,设字符串s, t的长度分别为n, m,对于,dp[i][0] = i,对于,dp[0][j] = j。
最后,在整个dp[][]数组规划完成后,取dp[n][m]即为答案。
代码实现
#include <bits/stdc++.h>
using namespace std;
int solution(std::string s, std::string t)2
{
int n=s.size(), m=t.size();
vector<vector<int>> dp(n+1,vector<int>(m+1));
s.insert(0,1,'0');
t.insert(0,1,'0');
for(int i=0;i<=n;i++) dp[i][0] = i;
for(int j=0;j<=m;j++) dp[0][j] = j;
for(int i=1;i<=n;i++)
{
for(int j=1;j<=m;j++)
{
dp[i][j] = min({dp[i-1][j]+1, dp[i][j-1]+1, dp[i-1][j-1] + (s[i]==t[j] ? 0 : 1)});
}
}
return dp[n][m];
}
总结
本题使用动态规划算法解决,设第一个字符串长度为,第二个字符串长度为,则时间复杂度为,空间复杂度为,如果你有复杂度更优的解法,欢迎在下方评论。