数据结构 : 动态规划学习二 | 字符串相似度

动态规划学习二

有关字符串的子序列或者配准问题
首先考虑 动态规划

2 字符串相似度/编辑距离(edit distance)

对于序列S和T,它们之间的距离定义为:对二者进行几次以下的操作

(1)删去一个字符 (2) 插入一个字符 (3) 改变一个字符

每进行一次操作,计数增加1.

将S和T变成同一个字符串的最小计数即为他们的距离

LeetCode 72. Edit Distance

递归解法: 超时!

int minDistance(string word1, string word2) {
    if(word1 == word2) return 0;

    int m = word1.size();
    int n = word2.size();

    if(word1 == "")
    {
        return n;
    }

    if(word2 == "")
    {
        return m;
    }

    if(word1[0] == word2[0])
    {
        return minDistance(word1.substr(1), word2.substr(1));
    }
    else
    {
        return min(1 + minDistance(word1, word2.substr(1)), min(1 + minDistance(word1.substr(1), word2), 1 + minDistance(word1.substr(1), word2.substr(1))));
    }

}

动态规划解法:

  • dp[i][j] 表示将 word1[0…j-1]变成word2[0…i-1]所需要的最少变化次数

  • 每次更新时,比较 word[j-1] 与 word2[i-1]

    若二者相同,则做小距离为 dp[i-1][j-1] .

    若不相等,则需要 求出 dp[i - 1][j - 1], dp[i - 1][j], dp[i][j - 1] 三者中的最小值, 加1 即可.

class Solution {
    public:
    int minDistance(string word1, string word2) {

        // 找到改变最少的次数将word1变成word2
        int n1 = word1.size();
        int n2 = word2.size();

        if (n1 == 0) {
            return n2;
        }
        if (n2 == 0) {
            return n1;
        }

        // dp[i][j] 表示将 word1[0...j-1]变成word2[0..i-1]最少需要变化次数
        vector<vector<int>> dp(n2 + 1, vector<int>(n1 + 1, INT_MAX));
        dp[0][0] = 0;

        // 第一行,word2为空,那么删除对应长度
        for (int i = 1; i < n1 + 1; i++) {
            dp[0][i] = i;
        }
        // 第一列,word1为空,那么需要增加对应的长度
        for (int i = 1; i < n2 + 1; i++) {
            dp[i][0] = i;
        }

        for (int i = 1; i < n2 + 1; i++) {
            for (int j = 1; j < n1 + 1; j++) {
                if (word1[j-1] == word2[i-1]) {
                    // 最后一个字符相等
                    dp[i][j] = dp[i - 1][j - 1];
                }
                else {
                    // 最后一个字符不等
                    int t= min(dp[i - 1][j - 1], min(dp[i - 1][j], dp[i][j - 1])) + 1;
                    dp[i][j] = t;
                }
            }
        }
        return dp[n2 ][n1 ];
    }
};

2.1 子串匹配

LeetCode 647. Palindromic Substrings

找出一个字符串中的所有回文字符串的个数.

class Solution {
    public:
    int countSubstrings(string s) {

        int n = s.size();
        if (n == 0) {
            return 0;
        }
        if (n == 1) {
            return 1;
        }

        vector<vector<bool>> dp(n , vector<bool>(n, false));

        // dp[j][i]表示 是否[i..j]的是回文串
        for (int j = 0; j < n; j++) {
            for (int i = 0; i <= j; i++) {
                // 递归公式 
                dp[j][i] = s[i] == s[j] && ((j - i) < 2 || dp[j - 1][i + 1]);
            }
        }

        int sum = 0;

        for (int j = 0; j < n; j++) {
            for (int i = 0; i <= j; i++) {
                sum += dp[j][i];
            }
        }
        return sum;
    }
};

参考文献

常见动态规划问题分析与求解

猜你喜欢

转载自blog.csdn.net/qjh5606/article/details/86547350