Python 动态规划算法求解最长公共子序列

前言:在网上看到一道360的秋招真题,题目如下:


仔细读题后发现这是一道求解最长公共子序列的问题,最好使用动态规划算法。

题目大意:

小B坐火车,从起点到终点的车站序列已知,期间他睡了两觉,到终点的时候还在睡,也就是说中间他醒了两次,这两次清醒的时间,有两个车站子序列,现在让我们分析这两段路是去的时候看到的,还是回来的时候看到的,来回都能看到,还是说压根不存在

思路:

一共有四种结果:

forward

backward

invalid

both

首先将两个子串连接,判断连接后的子串是否正好是与总串的最长公共子序列

若是,则forward验证

然后反转总串,再判断连接后的子串是否正好是与总串的最长公共子序列

若是,则backward验证

若都不是则不存在这样的车站序列

算法简介:

1、最长公共子序列的结构(问题的最优子结构性质)


2、子问题的递归结构(建立递归关系)

 由最优子结构性质可知,要找出XY的最长公共子序列可按以下方式递归地进行:

1)当xm=yn时,找出Xm-1Yn-1的最长公共子序列。

2)当xmyn时,必须解两个子问题,找出Xm-1Y的一个最长公共子序列及XYn-1的一个最长公共子序列。这两个公共子序列中较长者即为XY的一个最长公共子序列。

数据结构1:用C[i][j]记录序列和的最长公共子序列的长度


数据结构2:B[i][j]记录当前序列和的来源

(1)若C[i][j]由C[i-1][j-1]得到,B[i][j] = 1

(2)若C[i][j]由C[i-1][j]得到,B[i][j] = 2

(3)若C[i][j]由C[i][j-1]得到,B[i][j] = 3


Python2.7实现:

我的代码:

# encoding :utf-8


def lcs(k, l, slist, B):
    if k == 0 or l == 0:
        return
    if B[k][l] == 1:
        res.append(slist[k])
        lcs(k - 1, l - 1, slist, B)
    elif B[k][l] == 2:
        lcs(k, l - 1, slist, B)
    else:
        lcs(k - 1, l, slist, B)


def lcs2(k, l, sslist, B):
    if k == 0 or l == 0:
        return
    if B[k][l] == 1:
        res.append(sslist[k])
        lcs2(k - 1, l - 1, sslist, B)
    elif B[k][l] == 2:
        lcs2(k, l - 1, sslist, B)
    else:
        lcs2(k - 1, l, sslist, B)

while 1:
    fo = 0
    ba = 0
    s = raw_input()
    a = raw_input()
    b = raw_input()
    ss = s[::-1]
    slist = list(s)
    slist.insert(0, '0')
    ablist = list(a + b)
    ablist.insert(0, '0')
    sslist = list(ss)
    sslist.insert(0, '0')
    C = [([0] * len(ablist)) for i in range(len(slist))]
    B = [([0] * len(ablist)) for i in range(len(slist))]
    CC = [([0] * len(ablist)) for i in range(len(sslist))]
    BB = [([0] * len(ablist)) for i in range(len(sslist))]
    # print C
    # print slist
    # print ablist
    res = []
    for i in range(1, len(slist)):
        for j in range(1, len(ablist)):
            if slist[i] == ablist[j]:
                C[i][j] = C[i - 1][j - 1] + 1
                B[i][j] = 1
            elif C[i - 1][j] > C[i][j - 1]:
                C[i][j] = C[i - 1][j]
                B[i][j] = 3
            else:
                C[i][j] = C[i][j - 1]
                B[i][j] = 2
    lcs(len(slist) - 1, len(ablist) - 1, slist, B)
    print ablist
    print res
    if res[::-1] == ablist[1:]:
        fo = 1

    res = []
    for i in range(1, len(sslist)):
        for j in range(1, len(ablist)):
            if sslist[i] == ablist[j]:
                CC[i][j] = CC[i - 1][j - 1] + 1
                BB[i][j] = 1
            elif CC[i - 1][j] > CC[i][j - 1]:
                CC[i][j] = CC[i - 1][j]
                BB[i][j] = 3
            else:
                CC[i][j] = CC[i][j - 1]
                BB[i][j] = 2
    lcs2(len(sslist) - 1, len(ablist) - 1, sslist, BB)
    print res
    if res[::-1] == ablist[1:]:
        ba = 1

    if fo == 1 and ba == 0:
        print "forward"
    elif fo == 0 and ba == 1:
        print "backward"
    elif fo == 1 and ba == 1:
        print "both"
    else:
        print "invalid"
别人的代码:

while 1:
    a = raw_input()
    b = raw_input()
    c = raw_input()
    e = len(b)
    if (b in a) & (c in a):
        bl = a.find(b)
        if bl + e <= len(a) - 1:
            if a.find(c, bl + e) > 0:
                res = 1
            else:
                res = 0
        else:
            res = 0
    else:
        res = 0

    d = a[::-1]
    if (b in d) & (c in d):
        bl = d.find(b)
        if bl + e <= len(a) - 1:
            if d.find(c, bl + e) > 0:
                res += 2
            else:
                res += 0
        else:
            res += 0
    else:
        res += 0
    if res == 0: print'invalid'
    if res == 1: print'forward'
    if res == 2: print'backward'
    if res == 3: print'both'
看了人家的代码才体会到什么是差距,人家的代码简洁而又漂亮。这里再恶补一下find函数是怎么用的,发现没有,有了find函数,根本就不需要写求解最长公共子序列的函数了…

简介:find是字符串类型的成员函数

函数原型s.find(str, pos_start, pos_end)

解释:
  • str: 在s中被查找的“子串”
  • pos_start: 查找的首字母位置(从0开始计数。默认:0)
  • pos_end: 查找的末尾位置(默认-1)
返回值:如果查到:返回查找的 第一个出现的位置。 否则,返回-1

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值