2021-06-11:给定两个字符串s1和s2,问s2最少删除多少字符可以成为s1的子串? 比如 s1 = “abcde“,s2 = “axbc“。

Posted 福大大架构师每日一题

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了2021-06-11:给定两个字符串s1和s2,问s2最少删除多少字符可以成为s1的子串? 比如 s1 = “abcde“,s2 = “axbc“。相关的知识,希望对你有一定的参考价值。

2021-06-11:给定两个字符串s1和s2,问s2最少删除多少字符可以成为s1的子串? 比如 s1 = “abcde”,s2 = “axbc”。

福大大 答案2021-06-11:

解法一
求出str2所有的子序列,然后按照长度排序,长度大的排在前面。
然后考察哪个子序列字符串和s1的某个子串相等(KMP),答案就出来了。
分析:
因为题目原本的样本数据中,有特别说明s2的长度很小。所以这么做也没有太大问题,也几乎不会超时。
但是如果某一次考试给定的s2长度远大于s1,这么做就不合适了。
str1长度为N,str2长度为M。时间复杂度:O(2的M次方*N)。

解法二
生成所有s1的子串
然后考察每个子串和s2的编辑距离(假设编辑距离只有删除动作且删除一个字符的代价为1)
如果s1的长度较小,s2长度较大,这个方法比较合适。
时间复杂度:O(N * M平方)。

代码用golang编写。代码如下:

package main

import (
    "fmt"
    "sort"
    "strings"
)

func main() {
    s1 := "abcde"
    s2 := "axbc"
    ret1 := minCost1(s1, s2)
    ret3 := minCost3(s1, s2)
    fmt.Println(ret1, ret3)
}

// 题目:
// 给定两个字符串s1和s2,问s2最少删除多少字符可以成为s1的子串?
// 比如 s1 = "abcde",s2 = "axbc"
// 返回 1

// 解法一
// 求出str2所有的子序列,然后按照长度排序,长度大的排在前面。
// 然后考察哪个子序列字符串和s1的某个子串相等(KMP),答案就出来了。
// 分析:
// 因为题目原本的样本数据中,有特别说明s2的长度很小。所以这么做也没有太大问题,也几乎不会超时。
// 但是如果某一次考试给定的s2长度远大于s1,这么做就不合适了。
func minCost1(s1 string, s2 string) int {
    s2SubsVal := make([]string, 0)
    s2Subs := &s2SubsVal
    process(s2, 0, "", s2Subs)
    //s2Subs.sort(new LenComp());
    sort.Slice(*s2Subs, func(i, j int) bool {
        return len((*s2Subs)[i]) > len((*s2Subs)[j])
    })
    for _, str := range *s2Subs {
        if strings.Index(s1, str) != -1 { // indexOf底层和KMP算法代价几乎一样,也可以用KMP代替
            return len(s2) - len(str)
        }
    }
    return len(s2)
}

func process(str2 string, index int, path string, list *[]string) {
    if index == len(str2) {
        *list = append(*list, path)
        return
    }
    process(str2, index+1, path, list)

    pathbytes := []byte(path)
    pathbytes = append(pathbytes, str2[index])
    process(str2, index+1, string(pathbytes), list)
}

// 解法二的优化
func minCost3(s1 string, s2 string) int {
    if len(s1) == 0 || len(s2) == 0 {
        return len(s2)
    }
    M := len(s2)
    N := len(s1)
    dp := make([][]int, M)
    for i := 0; i < M; i++ {
        dp[i] = make([]int, N)
    }
    ans := M
    for start := 0; start < N; start++ { // 开始的列数
        if s2[0] != s1[start] {
            dp[0][start] = M
        }
        for row := 1; row < M; row++ {
            if s2[row] == s1[start] || dp[row-1][start] != M {
                dp[row][start] = row
            } else {
                dp[row][start] = M
            }
        }
        ans = getMin(ans, dp[M-1][start])
        // 以上已经把start列,填好
        // 以下要把dp[...][start+1....N-1]的信息填好
        // start...end end - start +2
        for end := start + 1; end < N && end-start < M; end++ {
            // 0... first-1 行 不用管
            first := end - start
            if s2[first] == s1[end] && dp[first-1][end-1] == 0 {

            } else {
                dp[first][end] = M
            }
            for row := first + 1; row < M; row++ {
                dp[row][end] = M
                if dp[row-1][end] != M {
                    dp[row][end] = dp[row-1][end] + 1
                }
                if dp[row-1][end-1] != M && s2[row] == s1[end] {
                    dp[row][end] = getMin(dp[row][end], dp[row-1][end-1])
                }
            }
            ans = getMin(ans, dp[M-1][end])
        }
    }
    return ans
}

func getMin(a int, b int) int {
    if a < b {
        return a
    } else {
        return b
    }
}

执行结果如下:
图片

以上是关于2021-06-11:给定两个字符串s1和s2,问s2最少删除多少字符可以成为s1的子串? 比如 s1 = “abcde“,s2 = “axbc“。的主要内容,如果未能解决你的问题,请参考以下文章

c_cpp 给定两个字符串S1和S2,要求判断S2能否被S1做循环移位得到的字符串包含。例如:s1 = AABCD和S2 = CDAA,返回true,s1 = ABCD和s2 = ACBD,返回fal

编程之美----字符串移位包含的问题

翻转子串

如何合并两个不同的字符串

第三章结构之法--------字符串移位包含

Python 给定字符串 求最长同类型字段和长度