Python 動態規劃演算法求解最長公共子序列
阿新 • • 發佈:2019-02-04
前言:在網上看到一道360的秋招真題,題目如下:
仔細讀題後發現這是一道求解最長公共子序列的問題,最好使用動態規劃演算法。
題目大意:
小B坐火車,從起點到終點的車站序列已知,期間他睡了兩覺,到終點的時候還在睡,也就是說中間他醒了兩次,這兩次清醒的時間,有兩個車站子序列,現在讓我們分析這兩段路是去的時候看到的,還是回來的時候看到的,來回都能看到,還是說壓根不存在。
思路:
一共有四種結果:
forward
backward
invalid
both
首先將兩個子串連線,判斷連線後的子串是否正好是與總串的最長公共子序列
若是,則forward驗證
然後反轉總串,再判斷連線後的子串是否正好是與總串的最長公共子序列
若是,則backward驗證
若都不是則不存在這樣的車站序列
演算法簡介:
1、最長公共子序列的結構(問題的最優子結構性質)
2、子問題的遞迴結構(建立遞迴關係)
由最優子結構性質可知,要找出X和Y的最長公共子序列可按以下方式遞迴地進行:
(1)當xm=yn時,找出Xm-1和Yn-1的最長公共子序列。
(2)當xm≠yn時,必須解兩個子問題,找出Xm-1和Y的一個最長公共子序列及X和Yn-1的一個最長公共子序列。這兩個公共子序列中較長者即為X和Y的一個最長公共子序列。
資料結構1:用C[i][j]記錄序列和的最長公共子序列的長度
資料結構2:用B[i][j]記錄當前序列和的來源
(1)若C[i][j]由C[i-1][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)