最长公共子序列(LCS)算法

实验内容

实现以下内容:

  1. 编程实现最长公共子序列(LCS)算法,并理解其核心思想。
  2. 时间复杂度 O(mn),空间复杂度 O(mn),求出 LCS 及其长度。
  3. 时间复杂度 O(mn),空间复杂度 O(2*min(m,n)),求出 LCS 的长度。
  4. 时间复杂度 O(mn),空间复杂度 O(min(m,n)),求出 LCS 的长度。

最长公共子序列 LCS:
给定两个字符串 text1 和 text2,返回这两个字符串的最长公共子序列的长度。
一个字符串的 子序列 是指这样一个新的字符串:它是由原字符串在不改变字符的相对顺序的情况下删除某些字符(也可以不删除任何字符)后组成的新字符串。
例如,“ace” 是 “abcde” 的子序列,但 “aec” 不是 “abcde” 的子序列。两个字符串的「公共子序列」是这两个字符串所共同拥有的子序列。
若这两个字符串没有公共子序列,则返回 0。
程序输入:
由控制台输入两个字符串 text1,text2。
其中:
1 <= text1.length <= 1000
1 <= text2.length <= 1000
输入的字符串只含有小写英文字符(a~z)

程序输出:
控制台打印 LCS 的长度及相应的序列,如不存在公共子序列则返回 0.
示例 1:
输入:text1 = “abcde”, text2 = “ace”
输出: LCS:“ace”, 长度:3
示例 2:
输入: text1 = “abc”, text2 = “def”
输出:0

算法设计思路

requirement_1

  1. 创建一个二维数组dp,其中dp[i][j]表示字符串s的前i个字符和字符串t的前j个字符的最长公共子序列的长度。
  2. 遍历字符串s和t的每个字符,如果s[i-1]等于t[j-1],则说明当前字符可以加入最长公共子序列中,因此dp[i][j]等于dp[i-1][j-1] + 1;否则,当前字符不能加入最长公共子序列,需要选择之前的最长子序列,即dp[i-1][j]和dp[i][j-1]中的较大值。
  3. 从dp数组中倒推得到最长公共子序列。从dp[m][n]开始,如果s[i-1]等于t[j-1],则将该字符添加到最长公共子序列中,并将i和j分别减1;否则,根据dp[i-1][j]和dp[i][j-1]的大小关系,更新i或j的值。
  4. 输出最长公共子序列及其长度。

requirement_2

  1. 创建一个二维数组dp,其中dp[i % 2][j]表示字符串s的前i个字符和字符串t的前j个字符的最长公共子序列的长度。由于每次只用到dp[(i-1) % 2][j-1]、dp[i % 2][j-1]和dp[(i-1) % 2][j]这三个值,因此可以使用滚动数组进行空间优化。
  2. 遍历字符串s和t的每个字符,如果s[i-1]等于t[j-1],则说明当前字符可以加入最长公共子序列中,因此dp[i % 2][j]等于dp[(i-1) % 2][j-1] + 1;否则,当前字符不能加入最长公共子序列,需要选择之前的最长子序列,即dp[(i-1) % 2][j]和dp[i % 2][j-1]中的较大值。
  3. 输出dp[m % 2][n],即最长公共子序列的长度。

requirement_3

  1. 创建一个一维数组dp,其中dp[j]表示字符串s的前i个字符和字符串t的前j个字符的最长公共子序列的长度。在每次更新dp[j]之前,先将其值保存到变量prev中。
  2. 遍历字符串s和t的每个字符,如果s[i-1]等于t[j-1],则说明当前字符可以加入最长公共子序列中,因此dp[j]等于prev + 1;否则,当前字符不能加入最长公共子序列,需要选择之前的最长子序列,即dp[j]和dp[j-1]中的较大值。
  3. 在更新dp[j]之前,将dp[j]的值保存到prev中以备后续使用。
  4. 输出dp[n],即最长公共子序列的长度。

源码及注释

def lcs(s, t):
    """
    计算最长公共子序列(LCS)的长度和序列本身
    参数:
    s:字符串类型,输入序列1
    t:字符串类型,输入序列2
    返回值:
    无返回值,直接打印LCS序列和其长度
    """
    m, n = len(s), len(t)
    dp = [[0] * (n+1) for _ in range(m+1)]
    for i in range(1, m+1):
        for j in range(1, n+1):
            if s[i-1] == t[j-1]:
                dp[i][j] = dp[i-1][j-1] + 1
            else:
                dp[i][j] = max(dp[i-1][j], dp[i][j-1])
    i, j = m, n
    lcs = ""
    while i > 0 and j > 0:
        if s[i-1] == t[j-1]:
            lcs = str(s[i-1]) + lcs
            i -= 1
            j -= 1
        elif dp[i-1][j] >= dp[i][j-1]:
            i -= 1
        else:
            j -= 1

    print(lcs)
    print(len(lcs))


def lcs_length(s, t):
    """
    计算最长公共子序列(LCS)的长度
    参数:
    s:字符串类型,输入序列1
    t:字符串类型,输入序列2
    返回值:
    打印LCS序列的长度
    """
    m, n = len(s), len(t)
    if m < n:
        s, t = t, s
        m, n = n, m
    dp = [[0] * (n+1) for _ in range(2)]
    for i in range(1, m+1):
        for j in range(1, n+1):
            if s[i-1] == t[j-1]:
                dp[i % 2][j] = dp[(i-1) % 2][j-1] + 1
            else:
                dp[i % 2][j] = max(dp[(i-1) % 2][j], dp[i % 2][j-1])
    print(dp[m % 2][n])


def lcs_length_1(s, t):
    """
    计算最长公共子序列(LCS)的长度
    参数:
    s:字符串类型,输入序列1
    t:字符串类型,输入序列2
    返回值:
    打印LCS序列的长度
    """
    m, n = len(s), len(t)
    if m < n:
        s, t = t, s
        m, n = n, m
    dp = [0] * (n+1)
    for i in range(1, m+1):
        prev = 0
        for j in range(1, n+1):
            temp = dp[j]
            if s[i-1] == t[j-1]:
                dp[j] = prev + 1
            else:
                dp[j] = max(dp[j], dp[j-1])
            prev = temp
    print(dp[n])


def main_1():
    """
    主函数,读取输入数据,调用各个函数计算最长公共子序列
    """
    with open('data.txt', 'r', encoding='utf-8') as file:
        lines = file.readlines()
        s = lines[0]
        t = lines[1]
        file.close()
    print("requirement_1:")
    lcs(s, t)
    print("requirement_2:")
    lcs_length(s, t)
    print("requirement_3:")
    lcs_length_1(s, t)
    
def main_2():
    """
    主函数,手动输入序列,调用各个函数计算最长公共子序列
    """
    s = 'asdasdfghjkl'
    t = 'fghasdfghjkl'
    print("requirement_1:")
    lcs(s, t)
    print("requirement_2:")
    lcs_length(s, t)
    print("requirement_3:")
    lcs_length_1(s, t)
    
    
if __name__ == "__main__":
    main_1()
  • 20
    点赞
  • 23
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值