1. 程式人生 > >Python 動態規劃演算法求解最長公共子序列

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