前言
最近工作中需要寫一個演算法,而寫完這個演算法我卻發現了一個很有意思的事情。需要的這個演算法是這樣的:對於A,B兩個字串,找出最多K個公共子串,使得這K個子串長度和最大。百度之沒有這樣的演算法,然後就開始想了一些亂七八糟的想法,一一被自己舉反例推翻了,直到最後找到了正確演算法,我覺得這個思考過程值得記錄一下。
思考過程
錯誤想法1:每次找最長公共子串,找到一個子串後,從A,B兩個字串中刪除這個子串,之後在剩下的串中再找最長公共子串,像這樣找K次。
舉個反例:
A=KABCDELMABCDEFGNFGHIJK
B=KABCDEFGHIJK
K=2
錯誤想法2:求A與B的最長公共子序列,之後從子序列中挑取最長的K段。
舉個反例:
A=EFGIJABC
B=ABCEFHIJ
K=1
正確解法:動態規劃
上面兩個看似取巧但是不對的想法被推翻後,也能讓我靜下心來進行系統性的思考了,正確解法為動態規劃。動態規劃最重要的事情有三件:找問題的狀態,找轉移方程,邊界初始化。找對狀態就相當於成功了一半,找到轉移方程基本問題就算解了,邊界初始化可以忽略不計。找狀態除了靠靈感之外,我最喜歡的方法是分解問題,找到問題的最原子的狀態,之後搭積木般的組合拼裝就OK了。
設dp[i][j][k]表示為以A[i],B[j]為第K個公共子串結尾時,所能得到的最大值。其中A[i]為字串A第i個字元,B[j]為字串B的第j個字元。
在考慮A[i]和B[j]時,如果A[i] = B[j],那麼A[i],B[j]可以單獨組成第K個串,也可以和A[i-1],B[j-1]組合在一起作為第K個串,則轉移方程如下:
dp[i][j][k] = dp[i-1][j-1][k] + 1 (與A[i-1],B[j-1]連在一起)
dp[i][j][k] = max(dp[i'][j'][k-1] + 1) (A[i],B[j]單獨成為第K個串)
dp[i][j][k] = max(dp[i-1][j-1][k] + 1, maxscore[i-1][j-1][k-1] + 1)
maxscore[i][j][k] = max(maxscore[i-1][j][k],maxscore[i][j-1][k], dp[i][j][k])