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

来源:互联网 发布:哈尔滨上牌数据 编辑:程序博客网 时间:2024/05/29 17:13

前言:在网上看到一道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-8def 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

原创粉丝点击