LintCode 解题记录17.8.4 字符串处理2

Distinct Distance

给定字符串S和T,求S中T的子串的个数。这里的子串是指不连续的子串。比如S=”rabbbit”,T=”rabbit”,那么S有三个rabbit子串。(中间的3个b有3种取2个b的取法)
思路
这一是一道动态规划的题目,我们维护的状态变量是S中前i个字符中T的前j个字符的个数dp[i][j],接下来的目标就是寻找递归关系。递推关系大部分都是dp[i][j]和dp[i-1][j-1],dp[i-1][j],dp[i][j-1]等这些关系。
首先把目光集中在了S中第i和字符和T中第j个字符的关系身上。如果S[i-1] != T[j-1],那么dp[i][j] = dp[i-1][j],意思就是如果在S[0:i-2]和T[0:j-1]满足题意的取法在S[0:i-1]和T[0:j-1]仍然满足。
可以理解为即使去掉S[i-1]这个字符也并不影响结果。如果S[i-1]==T[j-1],那么可以理解为dp[i-1][j-1]中的序列也仍然满足dp[i][j],此时dp[i][j] = dp[i-1][j]+dp[i-1][j-1]。
综合上述两种情况,dp[i][j] = dp[i-1][j] + (S[i-1]==T[j-1]? dp[i-1][j-1] : 0)
ps:可以自己举一个例子,然后画出如下二维矩阵,结合矩阵进行递推公式的寻找

  Ø r a b b b i t
Ø 1 1 1 1 1 1 1 1
r 0 1 1 1 1 1 1 1
a 0 0 1 1 1 1 1 1
b 0 0 0 1 2 3 3 3
b 0 0 0 0 1 3 3 3
i 0 0 0 0 0 0 3 3
t 0 0 0 0 0 0 0 3 

注意
边界条件。如果S为空,dp[0][j] = 0(j > 0),如果T为空,dp[i][0] = 1。
题目中要求了空间复杂度的优化,这在最基本的背包问题曾经了解过,意思就是倒序遍历。
此时公式简化为dp[j] = dp[j] + (S[i-1]==T[j-1] ? dp[j-1] : 0)。
倒序遍历保证在访问dp[j]时,dp[j],dp[j-1]还代表dp[i-1][j],dp[i-1][j-1]。而如果顺序遍历的话,dp[j-1]早于dp[j]被修改,代表的就不是dp[i-1][j-1]了。
代码

    int numDistinct(string &S, string &T) {
        // write your code here
        if (T == "") return 1;
        int lens = S.size(), lent = T.size();
        vector<int> dp(lent+1, 0);
        dp[0] = 1;
        for (int i = 1; i < lens+1; i++) {
            for (int j = lent; j >= 1; j--) {
                dp[j] = dp[j] + (S[i-1] == T[j-1] ? dp[j-1]: 0);
            }
        }
        return dp[lent];

    }

Edit Distance

编辑距离,具体概念请百度,这是一种用来衡量字符串距离的方式。
求给定两个字符串的编辑距离。
思路
也是动态规划的角度,维护的状态变量是dp[i][j],代表word1的前i个字符和word2的前j个字符的最小编辑距离。
同样可以先画如下矩阵用来方便理解

  Ø a b c d
Ø 0 1 2 3 4
b 1 1 1 2 3
b 2 2 1 2 3
c 3 3 2 1 2

同样把目光放在word1[i-1]和word2[j-1]上,如果word1[i-1] == word2[j-1],那么这两个字符便不需要编辑,此时dp[i][j]=dp[i-1][j-1]。如果word1[i-1] != word2[j-1],那么正如题意中说的,考虑前面的状态如何跳转可以达到该状态。题目中说有3种编辑方法。即如果在word1中插入一个字符串使得两者相等,即dp[i-1][j]+1,如果在word2中插入一个字符使得两者相等,即dp[i][j-1],以及替换一个字符达到该状态,即dp[i-1][j-1]。所谓动态规划,意思就是动着规划,没有固定的套路,每次都是选择最优的套路,即
dp[i][j] = min(dp[i-1][j], min(dp[i-1][j-1], dp[i][j-1])) + 1。
代码

    int minDistance(string word1, string word2) {
        // write your code here
        int len1 = word1.size(), len2 = word2.size();
        vector<vector<int>> dp(len1+1, vector<int>(len2+1, 0));
        for (int i = 0; i < len1+1; i++)
            dp[i][0] = i;
        for (int j = 0; j < len2+1; j++)
            dp[0][j] = j;
        for (int i = 1; i < len1+1; i++) {
            for (int j = 1; j < len2+1; j++) {
                if (word1[i-1] == word2[j-1]) {
                    dp[i][j] = dp[i-1][j-1];
                } else {
                    dp[i][j] = min(dp[i-1][j-1], min(dp[i-1][j], dp[i][j-1])) + 1;
                }
            }
        }
        return dp[len1][len2];
    }

Edit DistanceII

上一题的变种,给定两个字符串,问他们之间的编辑距离是不是小于1。
思路
直接求编辑距离然后判断当然是可以,但是此题有更简洁的做法。
首先先比较两个字符串的长度
1.如果长度之差大于1,那么编辑距离显然大于1。
2.如果长度之差等于0,那么只需要遍历一遍,比较相同位置的字符,只有有一个位置不同才满足题意。
3.如果长度之差等于1,那么顺序遍历一遍,如果遍历到两个字符不一样,那么长的那个就去掉该字符串,然后从新比较。
代码

    bool isOneEditDistance(string& s, string& t) {
        // Write your code here
        if (s.size() < t.size()) swap(s, t);
        int m = s.size(), n = t.size(), diff = m-n;
        if (diff > 1) return false;
        if (diff == 1) {
            for (int i = 0; i < n; i++) {
                if (s[i] != t[i]) {
                    return s.substr(i+1) == t.substr(i);
                }
            }
            return true;
        }
        int cnt = 0;
        for (int i = 0; i < n; i++) {
            if (s[i] != t[i]) cnt++;
        }
        return cnt == 1;
    }

可以经过修改让代码变简洁

    bool isOneEditDistance(string s, string t) {
        for (int i = 0; i < min(s.size(), t.size()); ++i) {
            if (s[i] != t[i]) {
                if (s.size() == t.size()) return s.substr(i + 1) == t.substr(i + 1);
                else if (s.size() < t.size()) return s.substr(i) == t.substr(i + 1);
                else return s.substr(i + 1) == t.substr(i);
            }
        }
        return abs(s.size() - t.size()) == 1;
    }
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值