算法:s2最少删除多少字符可以成为s1的子串?

题目描述

给定两个字符串s1和s2,问s2最少删除多少字符可以成为s1的子串?

题目解析

当str2长度远小于str1时

  • 生成s2的所有子序列:每个位置由两种选择—要、不要
  • 然后将所有子序列根据长度排序,长度大的在前面
  • 考察哪个子序列跟s1的某个字串相等(kmp)。
    • 假设s2长度为9
    • 长度为9的子序列只有1个,然后kmp发现返回-1,它不是s1的字串
    • 长度为8的子序列由若干个,它们都不是s1的字串
    • 长度为7的子序列时有一个是s1的字串,那么删除两个字符就可以了
    • 下面就不用去验证了

复杂度分析:

  • 假设s1的长度为N,s2的长度M
    • 第一步:生成所有子序列,子序列个数 2 M 2^M 2M,每个子序列都要生成字符串,所以代价为 O ( 2 M ∗ M ) O(2^M * M) O(2MM)
    • 第二步:对每个子序列都要做KMP,代价为 O ( 2 M ∗ N ) O(2^M * N) O(2MN)
  • 如果M不大,那么可以这样做
class Solution {
    void generatorSubs(std::string &s, int idx, std::string path, std::vector<std::string>&s2Subs){
        if(idx == s.size()){
            s2Subs.emplace_back(path);
            return;
        }

        generatorSubs(s, idx + 1, path, s2Subs);
        generatorSubs(s, idx + 1, path + s[idx], s2Subs);
    }
public:
    int minCost(std::string s1, std::string s2){
        std::vector<std::string> s2Subs;

        generatorSubs(s2, 0, "", s2Subs);
        std::sort(s2Subs.begin(), s2Subs.end(), [](std::string left, std::string right){
            return left.size() < right.size();
        });

        for(auto &str : s2Subs){
            // 待完成.................
        }

        return s2.length();
    }
};

当s2的长度比较大时

因为是s2要删除字符成为s1的字串,因此我们把所有s1的字串类出来,看s2能不能通过删除的方式到s1的每一个字串
在这里插入图片描述

用编辑距离优化。

实现:

class Solution {
    // x字符串只通过删除的方式,变到y字符串, 返回至少要删几个字符
    int onlyDelete(std::string x, std::string y){
        int M = x.size(), N = y.size();
//        if(M < N){
//            return INT32_MAX;
//        }

        std::vector<std::vector<int>> dp(M + 1, std::vector<int>(N + 1, INT32_MAX));
        dp[0][0] = 0;
        for (int i = 1; i <= M; ++i) {
            dp[i][0] = i;
        }

        // 对于当前字符,有两种决策:删/不删
        //        + 当x[i] == y[j]:只需要前i-1个字符变成前j-1个字符就可以
        //        + 当x[i] != y[j]:只需要前i-1个字符变成前j个字符,然后把x[i]删除就好了
        for (int i = 1; i <= M; ++i) {
            for (int j = 1; j <= std::min(i, N); ++j) {
                if(dp[i - 1][j] != INT32_MAX){
                    dp[i][j] = dp[i - 1][j] + 1;
                }


                if (x[i - 1] == y[j - 1] && dp[i - 1][j - 1] != INT32_MAX) {
                    dp[i][j] =  std::min(dp[i - 1][j - 1], dp[i][j]);
                }
            }
        }

        return dp[M][N];
    }
public:
    int minCost2(std::string s1, std::string s2){
        int ans = INT32_MAX;
        for (int start = 0; start < s1.length(); start++) {
            for (int end = start ; end < s1.length(); end++) {
                ans = std::min(ans, onlyDelete(s2, s1.substr(start, end - start + 1)));
            }
        }
        return ans == INT32_MAX ? s2.length() : ans;
    }
};

复杂度分析:

  • 假设s1的长度为N,s2的长度M
    • s1字串个数为 O ( N 2 ) O(N^2) O(N2)(因为每个字符都有两种选择)
    • s2对每一个s1的字串都有一张二维表,但是一共编辑距离问题
    • 所以一共有 N 2 N^2 N2张二维表,每张表的大小 M ∗ N M * N MN,是总的复杂度的是 O ( N 3 ∗ M ) O(N^3 * M) O(N3M)
  • 因为上面的解法是 O ( 2 M ∗ N ) O(2^M * N) O(2MN),所以张M小时选上面,M大时选本方法

优化

怎么只通过删除s2将s2变成s1的字串呢?

  • 首先要确保 s 2. l e n g t h > = s 1. l e n g t h s2.length >= s1.length s2.length>=s1.length
  • 标准的编辑距离有三种行为:插入、删除、替换
  • 在这里可以认为插入、替换无穷大,删除行为代价为1

对数器

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值