算法 - 动规12(Swift版本)

30 阅读2分钟

题目1:115. 不同的子序列

讲解

这里有个重点是, 如果字符相等 dp[i][j] = dp[i - 1][j] + dp[i - 1][j - 1]。
dp[i - 1][j - 1]好理解, 即选用了 s[i - 1], t[j - 1]。
dp[i - 1][j]对应的 其实就是不选用s[i - 1],那么就相当于 dp[i - 1][j]。
之所以存在 选用和不选用的区别 这里判断的是子序列的种类, 如果相等直接理解为选用的话, 就少计入了一部分情况。
比如下面的2:

image.png


class Solution {
    func numDistinct(_ s: String, _ t: String) -> Int {
        // dp[i][j] : s[0..<i] 中包含 t[0..<j] 的个数 
        // 如果 s[i - 1] == t[j - 1] : dp[i][j] = dp[i - 1][j] + dp[i - 1][j - 1]
        // 否则 dp[i][j] = dp[i - 1][j]
        if t.count == 0 { return 1 }
        if s.count == 0 { return 0 }
        var dp = Array(repeating: Array(repeating: 0, count: t.count + 1), count: s.count + 1)
        for i in 0...s.count {
            dp[i][0] = 1
        }
        var ss = Array(s)
        var ts = Array(t)
        for i in 1...s.count {
            for j in 1...t.count {
                if ss[i - 1] == ts[j - 1] {
                    dp[i][j] = dp[i - 1][j] + dp[i - 1][j - 1]
                } else {
                    dp[i][j] = dp[i - 1][j]
                }
            }
        }
        return dp[s.count][t.count]
    }
}

// 一维滚动。
class Solution {
    func numDistinct(_ s: String, _ t: String) -> Int {
        if t.count == 0 { return 1 }
        if s.count == 0 { return 0 }
        var dp = Array(repeating: 0, count: t.count + 1)
        dp[0] = 1
        let ss = Array(s)
        let ts = Array(t)
        for i in 1...s.count {
            let lastDp = dp
            for j in 1...t.count {
                if ss[i - 1] == ts[j - 1] {
                    // 神奇的 原地修改变量值,避免复制。可以AC
                    dp[j] &+= lastDp[j - 1]
                }
            }
        }
        return dp[t.count]
    }
}

题目2:583. 两个字符串的删除操作

讲解

如果字符相等, 那么不用操作。
如果字符不相等, 那么删除word1 或者 word2最后字符即可。

初始化是当前字符的个数。即删空。

class Solution {
    func minDistance(_ word1: String, _ word2: String) -> Int {
        var dp = Array(repeating: Array(repeating: 0, count: word2.count + 1), count: word1.count + 1)
        for i in 0...word1.count { dp[i][0] = i }
        for j in 0...word2.count { dp[0][j] = j }
        let word1s = Array(word1)
        let word2s = Array(word2)
        for i in 1...word1.count {
            for j in 1...word2.count {
                if word1s[i - 1] == word2s[j - 1] {
                    dp[i][j] = dp[i - 1][j - 1]
                } else {
                    dp[i][j] = min(dp[i - 1][j] + 1, dp[i][j - 1] + 1)
                }
            }
        }
        return dp[word1.count][word2.count]
    }
}

题目3:72. 编辑距离

讲解

相较于 583. 两个字符串的删除操作 多了一个更换字符的选择。

class Solution {
    func minDistance(_ word1: String, _ word2: String) -> Int {
        var word1s = Array(word1)
        var word2s = Array(word2)
        var dp = Array(repeating: Array(repeating: 0, count: word2.count + 1), count: word1.count + 1)
        for i in 0...word1.count { dp[i][0] = i }
        for j in 0...word2.count { dp[0][j] = j }
        if word1.isEmpty || word2.isEmpty { return dp[word1.count][word2.count] }
        for i in 1...word1.count {
            for j in 1...word2.count {
                if word1s[i - 1] == word2s[j - 1] {
                    dp[i][j] = dp[i - 1][j - 1]
                } else {
                    dp[i][j] = min(dp[i - 1][j - 1], dp[i - 1][j], dp[i][j - 1]) + 1
                }
            }
        }
        return dp[word1.count][word2.count]
    }
}