A really classic 2D DP problem. And I'm glad that I figured out the transfer function without checking any other resources.

After that, everything is so natural. But please take care of init states.

class Solution {
public:
    int minDistance(string word1, string word2) {
        int ret = 0;

        size_t len1 = word1.length();
        size_t len2 = word2.length();
        if (len1 * len2 == 0) return abs((int)len1 - (int)len2);

        //    dp[i][j] = min steps for word1[0..i-1] to word2[0..j-1]        
        //        word1[i-1] == word2[j-1]?
        //            dp[i][j] = dp[i-1][j-1]
        //        not equal
        //            dp[i][j] = min(dp[i-1][j-1], dp[i-1][j], dp[i][j-1]) + 1
        //        dp[len1][len2]

        vector<vector<int>> dp;
        dp.resize(len1 + 1);
        for (int i = 0; i <= len1; i++)
        {
            dp[i].resize(len2 + 1);
            std::fill(dp[i].begin(), dp[i].end(), 0);
        }
        for (int i = 1; i <= len1; i++) dp[i][0] = i;
        for (int j = 1; j <= len2; j++) dp[0][j] = j;
        
        //    Go
        for (int i = 1; i <= len1; i ++)
        for (int j = 1; j <= len2; j++)
        {
            if (word1[i - 1] == word2[j - 1])
            {
                dp[i][j] = dp[i - 1][j - 1];
            }
            else
            {
                int v1 = dp[i - 1][j - 1];
                int v2 = dp[i - 1][j];
                int v3 = dp[i][j - 1];
                dp[i][j] = std::min(std::min(v1, v2), v3) + 1;
            }
        }

        return dp[len1][len2];
    }
};