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

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章