深入解析最长公共子串

深入解析最长公共子串

什么是最长公共子串

最长公共子串(Longest Common Substring)是指两个或多个字符串中最长的子串,它可以用来比较两个字符串的相似程度。

例如,对于字符串 "abcdefg" 和 "defghij",它们的最长公共子串为 "defg",长度为 4。即 "abcdefg" 中的 "defg" 与 "defghij" 中的 "defg" 相同。

暴力解法

最简单的方法就是暴力枚举,我们依次枚举字符串 S1 和 S2 中的每个子串,然后判断该子串是否为它们的公共子串。最后找到长度最长的那个子串。

最坏时间复杂度为 $O(n^3)$,不适合处理大规模数据。

下面是暴力解法的伪代码:

for i from 0 to len(S1)
    for j from 0 to len(S2)
        for k from 0 to len(S1) - i and len(S2) - j
            if S1[i:i+k] == S2[j:j+k] and k > ans
                ans = k
                lcs = S1[i:i+k]
return lcs

动态规划解法

动态规划是一种时间复杂度为 $O(n^2)$ 的解法,它把问题拆解成若干个子问题,然后通过求解子问题的最优解,得到原问题的最优解。

对于字符串 S1 和 S2,我们定义二维数组 dp,其中 dp[i][j] 表示 S1[0:i] 和 S2[0:j] 的最长公共子串长度。

如果 S1[i] == S2[j],那么 dp[i][j] = dp[i-1][j-1] + 1,否则 dp[i][j] = 0。

下面是动态规划解法的伪代码:

dp = [[0 for _ in range(len(S2))] for _ in range(len(S1))]
ans = 0
lcs = ''
for i from 0 to len(S1)
    for j from 0 to len(S2)
        if S1[i] == S2[j]
            if i == 0 or j == 0
                dp[i][j] = 1
            else
                dp[i][j] = dp[i-1][j-1] + 1
        if dp[i][j] > ans
            ans = dp[i][j]
            lcs = S1[i-ans+1:i+1]
return lcs

示例解释

示例1

假设有字符串 S1 和 S2 分别为 "abcde" 和 "bcdfg",它们的最长公共子串为 "bcd",长度为 3。我们可以运用暴力解法和动态规划解法来进行验证。

暴力解法的伪代码为:

for i from 0 to len(S1)
    for j from 0 to len(S2)
        for k from 0 to len(S1) - i and len(S2) - j
            if S1[i:i+k] == S2[j:j+k] and k > ans
                ans = k
                lcs = S1[i:i+k]
return lcs

按照该算法步骤,有:

当 i=0, j=0, k=1 时,k=1, ans=1, lcs='a';

当 i=0, j=1, k=1 时,k=1, ans=1, lcs='b';

当 i=0, j=2, k=1 时,k=1, ans=1, lcs='c';

当 i=0, j=3, k=1 时,k=1, ans=1, lcs='d';

当 i=0, j=4, k=1 时,k=1, ans=1, lcs='e';

当 i=1, j=0, k=1 时,k=1, ans=1, lcs='b';

当 i=1, j=1, k=3 时,k=3, ans=3, lcs='bcd';

当 i=1, j=2, k=1 时,k=1, ans=3, lcs='bcd';

当 i=1, j=3, k=1 时,k=1, ans=3, lcs='bcd';

当 i=2, j=0, k=1 时,k=1, ans=3, lcs='bcd';

当 i=2, j=1, k=1 时,k=1, ans=3, lcs='bcd';

当 i=2, j=2, k=1 时,k=1, ans=3, lcs='bcd';

当 i=3, j=0, k=1 时,k=1, ans=3, lcs='bcd';

当 i=3, j=1, k=1 时,k=1, ans=3, lcs='bcd';

当 i=4, j=0, k=1 时,k=1, ans=3, lcs='bcd';

最后得到 lcs='bcd'。

动态规划解法的伪代码为:

dp = [[0 for _ in range(len(S2))] for _ in range(len(S1))]
ans = 0
lcs = ''
for i from 0 to len(S1)
    for j from 0 to len(S2)
        if S1[i] == S2[j]
            if i == 0 or j == 0
                dp[i][j] = 1
            else
                dp[i][j] = dp[i-1][j-1] + 1
        if dp[i][j] > ans
            ans = dp[i][j]
            lcs = S1[i-ans+1:i+1]
return lcs

按照该算法步骤,有:

当 i=0, j=0, S1[i]='a', S2[j]='b' 时,dp[0][0]=0;

当 i=0, j=1, S1[i]='a', S2[j]='c' 时,dp[0][1]=0;

当 i=0, j=2, S1[i]='a', S2[j]='d' 时,dp[0][2]=0;

当 i=0, j=3, S1[i]='a', S2[j]='e' 时,dp[0][3]=0;

当 i=0, j=4, S1[i]='a', S2[j]='f' 时,dp[0][4]=0;

当 i=1, j=0, S1[i]='b', S2[j]='b' 时,dp[1][0]=1;

当 i=1, j=1, S1[i]='b', S2[j]='c' 时,dp[1][1]=0;

当 i=1, j=2, S1[i]='b', S2[j]='d' 时,dp[1][2]=1;

当 i=1, j=3, S1[i]='b', S2[j]='e' 时,dp[1][3]=0;

当 i=1, j=4, S1[i]='b', S2[j]='f' 时,dp[1][4]=0;

当 i=2, j=0, S1[i]='c', S2[j]='b' 时,dp[2][0]=0;

当 i=2, j=1, S1[i]='c', S2[j]='c' 时,dp[2][1]=1;

当 i=2, j=2, S1[i]='c', S2[j]='d' 时,dp[2][2]=0;

当 i=2, j=3, S1[i]='c', S2[j]='e' 时,dp[2][3]=0;

当 i=2, j=4, S1[i]='c', S2[j]='f' 时,dp[2][4]=0;

当 i=3, j=0, S1[i]='d', S2[j]='b' 时,dp[3][0]=0;

当 i=3, j=1, S1[i]='d', S2[j]='c' 时,dp[3][1]=0;

当 i=3, j=2, S1[i]='d', S2[j]='d' 时,dp[3][2]=1;

当 i=3, j=3, S1[i]='d', S2[j]='e' 时,dp[3][3]=0;

当 i=3, j=4, S1[i]='d', S2[j]='f' 时,dp[3][4]=0;

当 i=4, j=0, S1[i]='e', S2[j]='b' 时,dp[4][0]=0;

当 i=4, j=1, S1[i]='e', S2[j]='c' 时,dp[4][1]=0;

当 i=4, j=2, S1[i]='e', S2[j]='d' 时,dp[4][2]=0;

当 i=4, j=3, S1[i]='e', S2[j]='e' 时,dp[4][3]=0;

当 i=4, j=4, S1[i]='e', S2[j]='f' 时,dp[4][4]=0;

最后得到 lcs='bcd'。

总结

动态规划解法是最长公共子串问题的高效解法,与暴力枚举法不同,它通过解决子问题的最优解,得到原问题的最优解。但是,用动态规划求解的空间和时间复杂度都比较高,如果 S1 和 S2 的长度较大,则需要引入优化、精简算法等措施处理。

如有问题,欢迎指正。

本站文章如无特殊说明,均为本站原创,如若转载,请注明出处:深入解析最长公共子串 - Python技术站

(1)
上一篇 2023年5月22日
下一篇 2023年5月22日

相关文章

  • C++实现产生随机数和相应的猜拳小游戏实例代码

    当我们玩游戏的时候,经常会需要用到随机数。在C++中,我们可以使用rand函数来生成随机数。为了演示如何使用rand函数来实现一个猜拳小游戏,下面我们将采取如下步骤: 注释掉程序中已有的代码段,以便写入新的代码。 导入头文件stdlib.h,包含了rand函数的定义。 引用时间函数time.h,以获得当前时间戳。 生成随机数,限定在0到2范围内,分别对应石头…

    C 2023年5月24日
    00
  • 深入解析C语言中的内存分配相关问题

    深入解析C语言中的内存分配相关问题 概述 在C语言中,内存分配是至关重要的。这是因为在C语言中,程序员需要手动地分配和释放内存以存储数据。C语言提供了几种内存分配方式,包括数据段、栈和堆。使用不当的内存分配方法可能导致程序运行时出现各种严重的问题,例如内存泄漏和段错误。本攻略将重点介绍C语言中的内存分配方式,并提供一些示例以帮助您更好地理解内存分配的概念。 …

    C 2023年5月23日
    00
  • Node.js 源码阅读深入理解cjs模块系统

    Node.js 源码阅读深入理解cjs模块系统的攻略可以分为以下几步: 1. 下载 Node.js 源代码 首先需要从 Node.js 官方网站下载 Node.js 的源代码。可以去 Node.js官网 下载最新版本的源代码,或者从 GitHub上的 Node.js仓库 上下载。下载后解压至本地,然后使用命令行工具进入解压后的目录。 2. 阅读 cjs 模块…

    C 2023年5月23日
    00
  • c++中的基本IO类型详解

    C++中的基本IO类型详解 概述 C++中的IO库为我们提供了丰富的输入输出功能,可以分为两大类:面向对象流和面向底层的文件操作。在这两类IO操作中,我们可以通过标准库中提供的多种数据类型和参数控制实现多功能和高效的输入输出。 面向对象流 cout与cin cout和cin是C++中最基本的标准输入输出流,分别用来输出数据和读取数据。 具体使用方式如下: #…

    C 2023年5月22日
    00
  • 解析C++中的字符串处理函数和指针

    解析C++中的字符串处理函数和指针 在C++中,字符串(String)是一种常见的数据类型。在使用字符串时,我们常常需要进行一些处理,例如拼接字符串、查找字符、截取子串等。此时,就需要用到字符串处理函数和指针。以下是详细的解析攻略。 字符串处理函数 在C++中,有一些常用的字符串处理函数,下面来一一介绍。 strlen strlen 函数用于计算字符串的长度…

    C 2023年5月23日
    00
  • C 语言基础之初识 C 语言常量

    下面是关于初识 C 语言常量的完整攻略。 什么是 C 语言常量 在 C 语言中,常量指的是固定不变的值,即程序运行期间不会改变的数据。常量可以分为两类:字面常量和符号常量。 字面常量 字面常量也叫直接常量,是指用数字、字符、字符串等直接表示的常量。 比如,以下是一些字面常量的例子: 42 // 整型常量 3.14 // 浮点型常量 ‘A’ // 字符型常量 …

    C 2023年5月24日
    00
  • c语言的指针数组详解

    c语言的指针数组详解 在C语言中,指针数组是一个非常重要的数据结构。它是由若干个指针组成的数组,每个指针存储了一个地址值,该地址指向一个具体的内存区域。通过指针数组,我们可以非常方便地管理多个指针,同时还可以用于实现动态内存分配和传递多个指针参数等情况。 定义指针数组 指针数组的定义格式为: 数据类型 *数组名称[数组长度]; 其中,数据类型表示指针指向的数…

    C 2023年5月23日
    00
  • c++实现简单的线程池

    c++实现简单的线程池,是一种常用的并发编程技术,用于提高程序的并行度和执行效率。下面我将为您提供实现线程池的完整攻略。 什么是线程池? 线程池是一种池化技术,用于管理和复用线程资源,避免频繁的线程创建和销毁。线程池中会预先创建一定数量的线程,并维护一个任务队列,当需要执行任务时,从队列中获取一个任务分配给线程执行。任务执行完毕后,线程回收到线程池中。 实现…

    C 2023年5月22日
    00
合作推广
合作推广
分享本页
返回顶部