給定兩個字符串,求出它們的最長公共字串git
var str1="abcdefg"; var str2="xyzabcd";
說明:好比在單詞"abcdefg"和"abcdefg"它們的最長公共子序列是"abcd"。尋找最長子序列經常使用於遺傳學中,用於使用核苷酸鹼基的首字母對DNA的描述(這段話從網上抄的)。github
最長公共子串和最長公共子序列的區別。數組
最長公共子串和最長公共子序列的區別爲:子串是串的一個連續的部分,子序列則是從不改變序列的順序,而從序列中去掉任意的元素而得到新的序列;也就是說,子串中字符的位置必須是連續的,子序列則能夠沒必要連續。微信
這道題,想了很長時間,終於慢慢的把題目作出來了,接下來的內容可能會很難理解,也許我比較笨吧至少對我來講想了很久。我會盡可能結合圖文去展現解題的思路,也能夠本身想一想,而後在看接下來的內容。優化
其實看到這個問題咱們直接能夠用暴力的方式解決這個問題。給定兩個字符串A和B,咱們能夠經過從A的第一個字符開始與B對應的每個字符進行對比的方式找到最長的公共字串。若是此時沒有找到匹的字母,則移動到A的第二個字符處,而後從B的第一個字符處進行對比,以此類推。因爲下面的內容較多,爆力方法我這裏就不寫了。spa
咱們回顧一下動態規劃的解題思路:code
從底部開始解決問題,將全部小問題解決掉,而後合併成一個總體的解決方案。rem
使用一個數組創建一張表,用於存放被分解成衆多子問題的解。字符串
那基於這兩點咱們首先要分解這個問題,怎麼分解呢?源碼
第一步: 最小的是每一個字符,因此要把分解成單個的字符去匹配每一個單個的字符。所以這裏咱們能夠獲得下面這一張表:
匹配爲1,不匹配爲0,這個時候咱們就分解成爲每一個字符的匹配狀況,由此獲得。
第二步: 每一個子問題有了,這個時候咱們要創建一個數組去存放每一個子問題的解。關鍵問題在於,咱們怎麼樣把每一個子問題的解存起來,而且能夠獲得咱們想要的結果。對錶作一些處理,初始化一個二維數組:
var arr = new Array(str1.length + 1); for (var i = 0; i <= str1.length + 1; i++) { arr[i] = new Array(str2.length + 1); for (var j = 0; j <= str2.length + 1; j++) { arr[i][j] = 0; } }
獲得以下的表:
圖中咱們能夠看到行和列都多加了一個而且都是0,這是爲了判斷上一個是否相等的操做,後面你就會明白它的意思了。
對初始化的二維數組坐些處理,獲得以下圖:
一開始看到這個圖的時候你可能會很懵逼,若是你已經看明白了,那你就跳過我這段廢話吧。咱們應該一直的告訴本身,新建的這個數組是存放每一個子問題的解,首先要明確的是找出最長字串,有哪些方式能夠作到把字串從原來的字符串中截取,因此要知道起始位置和字串的長度。從圖中能夠看到的紅字就是存放這個位置的最優解字串的長度,因此應該創建兩個變量去存儲其實位置的index 和最大長度 maxLen 。獲得一下代碼:
var maxLen = 0; var index = 0; for(var i = 0; i <= str1.length; i++){ for(var j = 0; j <= str2.length; j++){ if(i == 0 || j == 0){ arr[i][j] = 0 }else{ if (str1[i] == str2[j] && str1[i - 1] == str2[j - 1]) { arr[i][j] = arr[i - 1][j - 1] + 1; }else{ arr[i][j] = 0; } } if(arr[i][j] > maxLen){ maxLen = arr[i][j]; index = i; } } }
簡單的解釋一下,這裏要明確連續的字串的位置,是二維數組對角線上的值,這點明確了不少問題就好理解了。
上面的代碼把最主要的兩個參數找到了,那接下來就是選擇其中的一個字符串去截取字串:
var str = ""; if(maxLen == 0){ return ""; }else{ for(var k = index - maxLen; k < maxLen; k++){ str += str1[k]; } return str; }
下面貼上完整的代碼,你也能夠去個人github上查看源碼;
function LCS(str1, str2){ var maxLen = 0; var index = 0; var arr = new Array(); for (var i = 0; i <= str1.length + 1; i++) { arr[i] = new Array(); for (var j = 0; j <= str2.length + 1; j++) { arr[i][j] = 0; } } for(var i = 0; i <= str1.length; i++){ for(var j = 0; j <= str2.length; j++){ if(i == 0 || j == 0){ arr[i][j] = 0 }else{ if (str1[i] == str2[j] && str1[i - 1] == str2[j - 1]) { arr[i][j] = arr[i - 1][j - 1] + 1; }else{ arr[i][j] = 0; } } if(arr[i][j] > maxLen){ maxLen = arr[i][j]; index = i; } } } var str = ""; if(maxLen == 0){ return ""; }else{ for(var k = index - maxLen; k < maxLen; k++){ str += str1[k]; } return str; } } var str1="abcdefg"; var str2="xyzabcd"; LCS(str1, str2) // abcd
到此爲止咱們終於找到了最長公共字串。到這裏咱們須要想一想能不能在優化了,雖然這樣解決問題,可是引入了一個二維數組,在兩個字符串都較大的狀況下不是很划算,是否能夠進一步優化?答案是能夠的,須要改變一下策略,是否能夠創建一個一維數組就能夠解決問題了呢,這裏晚了先寫這麼多把,以後我會在公衆號中貼出暴力解法和動態規劃的優化解法,歡迎持續關注個人公衆號得到一手的信息。
歡迎關注微信公衆帳號查看最新文章