计算最长公共子序列LCS(Python实现)

最长公共子序列参考:https://blog.csdn.net/v_JULY_v/article/details/6110269

一、概念

两个字符串X=<x1, x2, …, xm>和Y=<y1, y2, …, yn>的最长公共子序列不要求在原字符串中是连续的,比如ADE和ABCDE的最长公共子序列是ADE。具体求解时关注于字符串最后一个字符:
在这里插入图片描述
相应的,用一个二维数组来表示两个字符串的LCS的长度。设c[i,j]存储X=(x1,…,xi)与Y=(y1,…,yj)的最长公共子序列的长度,这个长度值要根据下标i和j是否为0,以及两个字符串分别在第i和第j的字符是否相等来决定。

由LCS的递归公式可得长度的递归公式如下:i,j分别代表字符串X和Y的下标,xi表示字符串X第i个字符,yi表示字符串Y第i个字符,在字符串中,下标i,j从1开始,但在二维数组里,i,j要从0开始,c[i,j]表示字符串(x1,…,xi)和字符串(y1,…,yj)的LCS的长度。

因为字符串X,Y的下标从1开始,当i=0或j=0时,表示比较的两个字符串里至少有一个是空序列,那么一个字符串和一个空序列的LCS就是空序列,其长度自然是0,故c[i,j]=0。
在这里插入图片描述在这里插入图片描述
例如,设所给的两个序列为X=<A,B,C,B,D,A,B>和Y=<B,D,C,A,B,A>。由长度公式可得二维数组如下:


在这里插入图片描述

例如c[3,2]=1表示ABC和BD的LCS长度为1,计算时因为末尾字符C不等于D,故c[3,2] =max{c[2,2],c[3,1]}即max{AB和BD的LCS长度,ABC和B的LCS长度}。先填满第一行和第一列,再依次计算第二行直到第八行。c[7,6]即为字符串X和Y的LCS的长度。

此时可找到规律,首先就是二维数组的第一行和第一列那绝对是0,接下来固定i,开始遍历j,根据上图就是遍历第一行,若字符串X的第i个字符等于字符串Y的第j个字符,那么此c[i,j]的值就等于其相邻左上方对角线的值+1即c[i-1,j-1]+1;若字符串X的第i个字符不等于字符串Y的第j个字符,此c[i,j]就等于其相邻左边和上边哪个值最大,即max{c[i-1][j],c[i][j-1]},就这么依次遍历下去,直到填满每一个位置

遍历后由上图可知,字符串X,Y的LCS的长度为4,而且4出现了3次,表明有三个LCS的长度为4

二、实践

利用两个字符串最长公共子序列的长度,求两个字符串的相似度,在推荐中可用于过滤相似标题的实例。

  • 输入数据部分如下:
    在这里插入图片描述
  • 代码如下:
# -*- coding: utf-8 -*-
#!/usr/bin/python

import sys

def cal_lcs_sim(first_str, second_str):
    len_vv = [[0] * 50] * 50   %第一个50为列数,第二个50为行数,这里认为输入字符串的长度不会超过50,存储两个字符串对应位置的LCS长度

    first_str = unicode(first_str, "utf-8", errors='ignore')
    second_str = unicode(second_str, "utf-8", errors='ignore')

    len_1 = len(first_str.strip())
    len_2 = len(second_str.strip())

    for i in range(1, len_1 + 1):
        for j in range(1, len_2 + 1):
            if first_str[i - 1] == second_str[j - 1]: # 根据对应的字符是否相等来判断
                len_vv[i][j] = 1 + len_vv[i - 1][j - 1] # 长度二维数组的值
            else:
                len_vv[i][j] = max(len_vv[i - 1][j], len_vv[i][j - 1])

    return float(float(len_vv[len_1][len_2] * 2) / float(len_1 + len_2))  %两个字符串的相似度


for line in sys.stdin:
    ss = line.strip().split('\t')
    if len(ss) != 2:
        continue
    first_str = ss[0].strip()
    second_str = ss[1].strip()

    sim_score = cal_lcs_sim(first_str, second_str)
    print '\t'.join([first_str, second_str, str(sim_score)])

若字符串S1的长度为m,S2的长度为n,LCS(S1,S2)的长度为c,则相似度=(c * 2) / (m + n),将相似度缩放到[0,1]内,这是因为如果其中一个字符串很长,假设m = 1000,c = 1,那么相似度应该是很低;如果两个串长度一致且相同,那么相似度就该很高即100%相似。

  • 输出结果如下:
    在这里插入图片描述
  • 0
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值