Redis STRALGO LCS命令與實現

做者: Wen Hui
轉載:中間件小哥
在 Redis 6.0-rc4版本的reiease中,咱們看到 Redis支持一個新命令及其子命令: STRALO LCS, LCS是longest common subsequence(最長公共子序列)的縮寫,其定義是:一個數列{\displaystyle S},若是分別是兩個或多個已知數列的子序列,且是全部符合此條件序列中最長的,則{\displaystyle S}稱爲已知序列的最長公共子序列。例如x = [A,,B,C,B,D,A,B], y = [B,D,C,A,B,A],則序列[B,C,A]爲x和y的最長公共子序列。LCS在現實生活中有不少應用,例如檢測文本類似度,版本控制等,在生物醫學中,lcs也被用在檢測DNA樣本的類似度。Redis的做者salvatore也在他的twitter中提到使用使用·lcs測試covid-19冠狀病毒的基因序列。(https://twitter.com/antirez/status/1245448546205216777)。
STRALGO LCS命令格式以下:
STRALGO LCS [KEYS ...] [STRINGS ...] [LEN] [IDX] [MINMATCHLEN <len>] [WITHMATCHLEN]
借用官網的例子,若是咱們計算ohmytext和mynnewtext兩個字符串的lcs,可使用以下命令:redis

STRALGO LCS STRINGS ohmytext mynewtext
"mytext"
咱們也能夠計算兩個鍵相對應的字符串值的lcs,經過使用keys參數,例子以下:
MSET key1 ohmytext key2 mynewtext
OK
STRALGO LCS KEYS key1 key2
"mytext"
咱們也可使用len參數只計算lcs的長度而不返回具體的lcs字符串:
STRALGO LCS STRINGS ohmytext mynewtext LEN
6
另外,咱們也能夠經過idx參數獲得每個lcs字符的位置。
STRALGO LCS KEYS key1 key2 IDX
1) "matches"
2) 1) 1) 1) (integer) 4
2) (integer) 7
2) 1) (integer) 5
2) (integer) 8
2) 1) 1) (integer) 2
2) (integer) 3
2) 1) (integer) 0
2) (integer) 1
3) "len"
4) (integer) 6
如上所示,字符串key1的值(ohmytest)和key2的值(mynewtest)的lcs結果爲字符串1的4-7索引和字符串2的5-8的索引(對應着test)和字符串1的2-3的索引和字符串2的0-1的索引(對應着my)。由於計算的時候是從後往前計算的,因此輸出的結果也是相反的。
咱們也能夠經過提供minmatchlen參數指定最小的匹配長度,以下所示:
STRALGO LCS KEYS key1 key2 IDX MINMATCHLEN 4
1) "matches"
2) 1) 1) 1) (integer) 4
2) (integer) 7
2) 1) (integer) 5
2) (integer) 8
3) "len"
4) (integer) 6
這樣字符串1的2-3的索引和字符串2的0-1的索引(對應着my)的匹配就被過濾掉了。
求解LCS及其長度自己是個·np-hard問題,但能夠經過使用空間換時間的思想使用動態規劃在多項式時間來求解。
具體思路以下,假如咱們要求數組x [x1,x2,x3,x4 … xm]和數組y [y1,y2,y3,y4 … yn]的lcs長度,若是xm 和yn的值相等,那麼原問題便轉化爲x的子數組x [x1,x2,x3,x4 … xm-1] 和y [y1,y2,y3,y4 … yn-1]的lcs長度加一。相反地,若是xm 和yn的值不相等,則lcs的長度爲lcs(x[x1,x2,x3,x4 … xm-1], y[y1,y2,y3,y4 … yn]) 和lcs(x[x1,x2,x3,x4 … xm], y[y1,y2,y3,y4 … yn-1])的較大值。綜上所述,經過轉換爲子問題來求解,狀態轉移方程以下:算法

Redis STRALGO LCS命令與實現

咱們在具體實現時,能夠經過創建動態規劃表,來存儲以前計算過的兩個前綴字串的lcs長度。例如若是咱們要計算mynewtest和ohmytest的lcs長度,則動態規劃表以下:數組

Redis STRALGO LCS命令與實現

咱們從上向下,從左向右填充填充動態規劃表,須要注意的是邊界狀況。在兩個前綴子串有一個爲空的時候,lcs的長度也爲0.當xi和yj相同時,則當前lcs長度爲左上角的方格的值加1,當xi和yj不相同時,則當前lcs長度爲左邊或右邊方格中的值的最大值。這樣,當處理到最右下角的方格時,計算出的值就爲兩個字符串的lcs長度。
接下來咱們要考慮的是若是知道lcs長度的動態規劃表,怎樣來獲得lcs呢?咱們能夠從後往前查找,若是當前位置i,j知足xi 等於yj時,記錄當前值到result數組裏,並將i和j各減一。若是當前位置xi和yj不相等,則查找動態規劃表,若是lcs(i-1,j)大於lcs(i,j-1)時,i減一,反之j減一。重複以上過程直到i或j有一個爲零爲止。下表記錄了計算lcs的過程。ide

Redis STRALGO LCS命令與實現

其中紅色表明記錄當前字符到結果數組,箭頭表明i和j的移動方向。
Redis的lcs命令實現,就是經過以上算法實現的,具體代碼和詳細註釋以下:測試

Redis STRALGO LCS命令與實現

Redis STRALGO LCS命令與實現

Redis STRALGO LCS命令與實現

Redis STRALGO LCS命令與實現

Redis STRALGO LCS命令與實現

參考資料:
https://zh.wikipedia.org/wiki/%E6%9C%80%E9%95%BF%E5%85%AC%E5%85%B1%E5%AD%90%E5%BA%8F%E5%88%97
算法導論第三版 15.4節
https://redis.io/commands/stralgoui

相關文章
相關標籤/搜索