文章開頭先上demo,只需鍵入任意內容的兩個字符串,頁面上就能自動計算並呈現字符串之間的差分。html
demo地址:string-diff-demo.herokuapp.comgit
源碼地址:github.com/lqt0223/str…github
動態規劃(dynamic programming)是你們在算法學習中都會遇到的話題之一。我我的對於它的理解是:算法
能夠被動態規劃解決的問題,常見的有:數組
曾經,我本身在codewar等網站上作算法題時,不少次刷到"longest common sequence"或者相似的題目,也經過一些算法書瞭解到這類問題的一個比較易於理解的算法是動態規劃。但我一直不太明白這類問題的實際應用何在。直到最近看到了下面的論文:瀏覽器
An O(ND) Difference Algorithm and Its Variations - EUGENE W. MYERS數據結構
此文我只讀了其中的1-2節,總結一下它的內容實際上是:使用圖算法,求解兩個字符串之間的LCS,以及最短編輯步驟(shortest edit script,如下簡稱SES,指的是從字符串A變換至字符串B,所須要的步驟。步驟是針對字符串的操做,例如刪除某一位置上的字符、在某一位置上插入字符等)。app
今後文可知:LCS和SES是對偶問題(dual problem),這兩個問題只不過是一個優化問題的兩個方面。即,當咱們尋找兩個字符串的公共子序列時,若是已經找到了最優解(最長公共子序列),那麼在此最優解情形下的兩個字符串之間的編輯步驟,也就是最短編輯步驟。通俗地講,求解LCS的過程當中,咱們就能夠獲得SES。工具
因爲SES描述了從一個字符串到另外一個字符串的一系列操做步驟,這就相似於各種數據比較工具產生的差量數據。因而咱們知道了,LCS問題的實際應用之一,就是數據的比較、差量計算和差量更新。學習
因爲是用動態規劃來求解LCS和SES問題,咱們須要用到矩陣(二維數組)來記錄最優解的一些信息。
這一小節主要是說明在使用矩陣求解以上問題的過程當中,矩陣有哪些性質,以及這些性質對應着LCS或SES問題的什麼方面。這些內容也是對於上一小節中提到的論文第2節內容的概括和簡化。
若是以前沒有接觸過使用動態規劃求解LCS問題的話,能夠看一下下面的視頻,從而對於這一求解過程有一個基本概念。
Longest Common Subsequence - Tushar Roy - Youtube
整體來講,使用矩陣轉化並求解LCS和SES問題須要如下三個階段:
通過三個階段後,矩陣會變成相似下圖的形式。
圖中是字符串A爲"abcabba",字符串B爲"cbabac"時,使用動態規劃求解LCS和SES造成的矩陣。由此矩陣咱們能夠得出如下關於兩個字符串之間的LCS和SES的相關答案:
例:字符串A爲"abcabba",字符串B爲"cbabac"時,如何知道通過什麼樣的步驟,能夠最快地將字符串A變爲字符串B呢?咱們可使用上面的規則,將紅色路徑翻譯成咱們須要的SES
- 刪除字符串A的第一、2個字符(最左上角的兩個向左箭頭)
- 在字符串A的第3個位置添加字符"b"(從左上至右下的第四個向上箭頭)
- 刪除字符串A的第6個字符(從左上至右下的倒數第三個向左箭頭)
- 在字符串A的第7個位置添加字符"c"(最右下角的向上箭頭)
通過上述操做後咱們就能夠將字符串A變換爲字符串B
上一節的末尾給出了從"abcabba"到"cbabac"的SES,也許你試着用草稿紙或者其餘工具來使用這段SES,但卻沒法順利地完成字符串的轉換。這是由於:SES所表示的編譯步驟,須要被同時操做。這個說法比較抽象,下面使用"abcabba"到"cbabac"例子,說明SES的正確用法:
原字符串
a b c a b b a 複製代碼
刪除字符串A的第一、2個字符(最左上角的兩個向左箭頭)(這裏用*標記將要被刪除的字符)
* * c a b b a 複製代碼
在字符串A的第3個位置添加字符"b"(從左上至右下的第四個向上箭頭)
* * c a b b a b 複製代碼
刪除字符串A的第6個字符(從左上至右下的倒數第三個向左箭頭)
* * c a b * a b 複製代碼
在字符串A的第7個位置添加字符"c"(最右下角的向上箭頭)
* * c a b * a b c 複製代碼
將以上相似於hashTable的結構還原爲一個字符串,規則爲:遇到須要刪除的字符時則忽略,遇到縱向伸展的list時將其連綴爲一個子字符串,最後將全部子字符串按順序鏈接,即獲得"cbabac"
由此可知,SES的同時操做,指的是任何一個操做步驟,都不該該影響到字符串最初的字符排列。咱們能夠用這種縱向的數據結構,從新整理字符串操做,並在最後轉換成目標字符串。
如上一小節所示,SES的應用之一就是直接執行,其結果就是生成目標字符串。
咱們也能夠結合原字符串和SES,生成DOM String,在瀏覽器中將原字符串到目標字符串的差分呈現出來。本文開頭的demo便是對於這種應用方式的展現。
不只是字符級的diff & patch,若是在不考慮算法空間複雜度的狀況下,動態規劃也能夠簡單地實現單詞級、行級的diff & patch。
學習和實現這個算法給我最大的體會是: