题解——25. DNA序列编辑距离 | 豆包MarsCode AI刷题

42 阅读3分钟

原题截图

image.png

题意分析

有两个字符串,有三种操作:1. 在字符串的任意位置添加任意一个字符;2. 删除字符串中的任意一个字符;3. 将字符串中的任意一个字符修改为任意的其他字符。问最少需要多少次操作,能将第一个字符串变为第二个字符串。

解题思路

本题是一道经典算法问题:编辑距离(但由于本人动态规划较弱,思考较长时间后,还是上网查询了题解才做出本题)。题目的含义较为简单,即最少多少次增、删、改操作后能将一个字符串变为另一个字符串。经过思考可以发现,本题难以使用简单的贪心算法得出最优解,于是考虑使用动态规划

使用动态规划解决本题时,可以定义二维数组int dp[][],设第一个字符串为s,第二个字符串为tdp[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个字符(如果最后一个字符和tj个字符相同则无需修改)。

这三种方法之一,变为字符串t的前j个字符,最小操作次数即为上述3种操作所需次数的最小值。 对于初始状态,可以用dp[i][0]代表从字符串s的前i个字符变为空串所需的操作次数,用dp[0][j]代表从空串变为字符串t的前j个字符所需的操作次数,容易得出,设字符串s, t的长度分别为n, m对于0in0\leq i\leq ndp[i][0] = i,对于0jm0\leq j\leq mdp[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];
}

总结

本题使用动态规划算法解决,设第一个字符串长度为nn,第二个字符串长度为mm,则时间复杂度为O(nm)O(nm),空间复杂度为O(nm)O(nm),如果你有复杂度更优的解法,欢迎在下方评论。