动态规划类问题的进一步理解,动规与递归等。

    以前对动规的理解太浅了,总是直接把当前状态保存,然后下一次遍历状态,然后更新状态。这是基于之前写 指数次循环的解法里总结的。
    于是就对一些动规的题都直接这样套,造成的问题就是,如果迭代时每个子状态再生成N个子状态,那么要存储的变量就太多太大了!会造成时间和空间上的双重问题。
    正确的思路应该是根据状态的属性寻找状态空间,寻找重复状态!确定状态量属性范围,确定遍历方式,确定状态前后的转移关系。

实例

看LeetCode 72. Edit Distance:
给定两个单词 word1 和 word2,计算出将 word1 转换成 word2 所使用的最少操作数 。
你可以对一个单词进行如下三种操作:

  • 插入一个字符
  • 删除一个字符
  • 替换一个字符

示例 1:
输入: word1 = “horse”, word2 = “ros”
输出: 3
解释:
horse -> rorse (将 ‘h’ 替换为 ‘r’)
rorse -> rose (删除 ‘r’)
rose -> ros (删除 ‘e’)

一开始我想岔了,以为需要先找最长重复子序列,后来发现直接dp就行,然后先写了递归的版本。然后TLE了。
于是意识到应该是递归函数栈调用超时。开始递归转循环,于是就要找循环的遍历范围,状态空间是word1的每一个位置和word2的每一个位置对应一个状态量最小操作数。遍历方式就两个嵌套循环。状态转移关系与递归类似的。
然后又在discussion里找到了一种空间使用更少的dp方案。把外循环的量只用一个临时变量存下。

代码:

class Solution {
public:
    bool recursive(string word1, string word2, int num, int& minc) {
        if (word1 == word2) {
            minc = num < minc ? num : minc;
            return true;
        }
        if(word2.size() < 1) {
            num += word1.size(); // delete n
            minc = num < minc ? num : minc;
            return true;
        } 
        if (word1.size() < 1) { // word2.size() != 0 
            num += word2.size(); // insert n
            minc = num < minc ? num : minc;
            return true;
        }
        // 一种加速方案,过滤掉比之前方案还差的
        if (num > minc) {
            return false;
        }
        if (word1[0] == word2[0]) {
            recursive (word1.substr(1, word1.size()-1), word2.substr(1, word2.size()-1), num, minc);
        } else {
            /// delete
            recursive (word1.substr(1, word1.size()-1), word2, num+1, minc);
            /// replace
            recursive (word1.substr(1, word1.size()-1), word2.substr(1, word2.size()-1), num+1, minc);
            /// insert 
            recursive (word1, word2.substr(1, word2.size()-1), num+1, minc);
        }
        return true;

    }
    int dpfind(string word1, string word2) {
       // 循环的dp,
       // 每次三种选择,需要储存每种的选择的位置和操作总次数。这样就3^n了。。。
       // 状态量还与word的位置有关,word1 的每一个位置对应word2的每一个位置,就包含了所有状态量,
       // 寻找重复状态!确定状态量属性范围,确定遍历方式,确定状态转移关系。
        int **dp;
        dp = new int* [word1.size()+1];
        for (int i = 0; i < word1.size()+1; ++i) {
            dp[i] = new int [word2.size()+1];
        }
        dp[0][0] = 0; // 序号==原长度-剩余长度
        for (int i = 0; i < word1.size()+1; ++i) {
            dp[i][0] = i;
        }
        for (int i = 0; i < word2.size()+1; ++i) {
            dp[0][i] = i;
        }
        for (int i = 1; i < word1.size()+1; ++i)
            for (int j = 1; j < word2.size()+1; ++j) {
                if (word1[i-1] == word2[j-1]) {
                    dp[i][j] = dp[i-1][j-1];
                } else {
                    // 上一次的 delete
                    int de = dp[i-1][j]+1;
                    // 上一次的 replace
                    int re = dp[i-1][j-1] +1;
                    // 上一次的 insert
                    int ie = dp[i][j-1] +1;
                    dp[i][j] = min(min(de, re),ie);
                }
            }
        return dp[word1.size()][word2.size()];

    }
    // dicussing 更少存储的一种dp
    int dpfindmin(string word1, string word2) {
        int m = word1.length(), n = word2.length();
        vector<int> cur(m + 1, 0);
        for (int i = 1; i <= m; i++)
            cur[i] = i;
        for (int j = 1; j <= n; j++) {
            int pre = cur[0];
            cur[0] = j;
            for (int i = 1; i <= m; i++) {
                int temp = cur[i];
                if (word1[i - 1] == word2[j - 1])
                    cur[i] = pre;
                else cur[i] = min(pre + 1, min(cur[i] + 1, cur[i - 1] + 1));
                pre = temp;
            }
        }
        return cur[m]; 
    }


    int minDistance(string word1, string word2) {
        /// 1. find longest subsequece ..balbla
        /// 2. dp. try all
        /// max = dist(length1-length2) + length2
        /// find longest is wrong. dp 
        /// dp: 1.try a.replace it with same one
        ///     2. try remove it
        ///     3.insert same one
        ///     4. if pre is same, 1 2 3 可以略过
        /// 先写递归,再写循环版的
        int ans = INT_MAX;
        //recursive (word1, word2, 0, ans);
        ans = dpfind(word1,word2);
        return ans;
    }
};

用循环写递归还要多加练习,尤其是这种要寻找dp的状态空间范围的。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值