利用矩阵求两个字符串的最长公共子串

分析

假设两个字符串,需要求两个字符串的最长公共子串,我们利用矩阵方式来看

str1 = 'abcdfg'
str2 = 'abfcdfga'

通过两个字符串的长度得到一个初始值都为0的矩阵

import numpy as np
lcs_np = np.zeros((len(str1), len(str2)))
>>>
[[0. 0. 0. 0. 0. 0. 0. 0.]
 [0. 0. 0. 0. 0. 0. 0. 0.]
 [0. 0. 0. 0. 0. 0. 0. 0.]
 [0. 0. 0. 0. 0. 0. 0. 0.]
 [0. 0. 0. 0. 0. 0. 0. 0.]
 [0. 0. 0. 0. 0. 0. 0. 0.]]

通过循环,str1和str2字符串,当值相等时,对应位置的值就赋值为1

for i in range(len(str1)):
    for j in range(len(str2)):
        if str1[i] == str2[j]:
            # 2.循环对比两个字符串各位置是否相等,相等的话,对应位置赋值为1,
            lcs_np[i, j] = 1
print(lcs_np)
>>>
[[1. 0. 0. 0. 0. 0. 0. 1.]
 [0. 1. 0. 0. 0. 0. 0. 0.]
 [0. 0. 0. 1. 0. 0. 0. 0.]
 [0. 0. 0. 0. 1. 0. 0. 0.]
 [0. 0. 1. 0. 0. 1. 0. 0.]
 [0. 0. 0. 0. 0. 0. 1. 0.]]

第一行第一列为1,下标为[0,0], 表示str1[0] = str2[0]
第一行第8列为1,下标为[0,7], 表示str1[0] = str2[7]
以此类推...

从上述矩阵可以得到一个结论:
连续i子串的特点就是如果str1[i]和str2[j]是属于某公共子串的最后一个字符,那么一定有str1[i]=str2[j] && str1[i-1] = str2[j-1],从矩阵中直观的看,就是由“1”构成的“斜线”代表的序列都是公共子串,那么最长公共子串肯定就是斜线“1”最长的那个串。

由此得到:
如果str1[i] = str2[j],那么str1[i] 和 str2[j]公共子串的长度必然是包含str1[i-1]和str2[j-1]的公共子串的长度加1

for i in range(len(str1)):
    for j in range(len(str2)):
        if str1[i] == str2[j]:
            lcs_np[i, j] = 1
            # 计数相等的个数, 如果str1[i] = str2[j],那么str1[i] 和 str2[j]公共子串的长度必然是包含str1[i-1]
            # 和str2[j-1]的公共子串的长度加1
            lcs_np[i, j] = lcs_np[i - 1][j - 1] + 1

print(lcs_np)
>>>
[[1. 0. 0. 0. 0. 0. 0. 1.]
 [0. 2. 0. 0. 0. 0. 0. 0.]
 [0. 0. 0. 1. 0. 0. 0. 0.]
 [0. 0. 0. 0. 2. 0. 0. 0.]
 [0. 0. 1. 0. 0. 3. 0. 0.]
 [0. 0. 0. 0. 0. 0. 4. 0.]]

由上述矩阵可以得出,最长的公共子串长度为最大值 4,只要找到4的位置即可得到最长的公共子串最后一个值的位置,往前再取三个值就是我们要的最长公共子串了

lcs_np = np.zeros((len(str1), len(str2)))
for i in range(len(str1)):
    for j in range(len(str2)):
        if str1[i] == str2[j]:
            # 2.循环对比两个字符串各位置是否相等,相等的话,对应位置赋值为1,
            lcs_np[i, j] = 1
            # 3. 计数相等的个数, 如果str1[i] = str2[j],那么str1[i] 和 str2[j]公共子串的长度必然是包含str1[i-1]
            # 和str2[j-1]的公共子串的长度加1
            lcs_np[i, j] = lcs_np[i - 1][j - 1] + 1
m = np.argmax(lcs_np)
r, c = divmod(m, lcs_np.shape[1])
max = np.max(lcs_np)
start_ = int(c - max)
end_ = c + 1
lcs = str1[start_: end_]

整体代码

import numpy as np


def Lcs(str1: str, str2: str) -> str:
    """
    求两个字符串的最长公共子串
    :param str1: 第一个字符串
    :param str2: 第二个字符串
    :return:
    """
    # 1. 得到一个由两个字符串长度组成的值都为0的矩阵
    lcs_np = np.zeros((len(str1), len(str2)))
    for i in range(len(str1)):
        for j in range(len(str2)):
            if str1[i] == str2[j]:
                # 2.循环对比两个字符串各位置是否相等,相等的话,对应位置赋值为1,
                lcs_np[i, j] = 1
                # 3. 计数相等的个数, 如果str1[i] = str2[j],那么str1[i] 和 str2[j]公共子串的长度必然是包含str1[i-1]
                # 和str2[j-1]的公共子串的长度加1
                lcs_np[i, j] = lcs_np[i - 1][j - 1] + 1
    m = np.argmax(lcs_np)
    r, c = divmod(m, lcs_np.shape[1])
    max = np.max(lcs_np)
    start_ = int(c - max)
    end_ = c + 1
    lcs = str1[start_: end_]
    return lcs


def main():
    """
    求两个字符串的最长公共子串
    :return:
    """
    str1 = 'abcdfg'
    str2 = 'abfcdfga'
    lcs = Lcs(str1, str2)
    print(f'最长公共子串为 {lcs}')


if __name__ == '__main__':
    main()

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值