Homework 11-8 = ###### tags: `Course - Algorithmics` ## Design 偷懶一下,把LCS當成LIS來解,這邊複製貼上一下程式作業4的內容: 1. 用Hunt–Szymanski algorithm把兩個字串合成成一串數字。 e.g. 首先把兩個字串看成一個2維坐標系,在字元相同的座標打上星號並記錄下來。 ![](https://i.imgur.com/DfjhH45.png) 然後對這些座標做Counting Sort ![](https://i.imgur.com/l6iUTzC.png) 2. 接下來就是畫圖,因為我們要找LIS,所以只能由小的頂點接到大的頂點,詳細規則如下: 1. 每個數字都是一個頂點。 2. 若以下條件成立,則頂點A有一條連到B的邊 (A→B): * 原序列中,B在A的右邊。 * B的值比A大 (B.value > A.value)。 3. 需要一個額外的頂點當作終點 (end)。 4. 每個頂點都有一條連到終點的邊 (e.g. A→end)。 上面舉的例子畫成圖長這樣: ![](https://i.imgur.com/2ZUVGaD.png) 3. 這張圖是一張DAG,既然要找最長序列,那畫成圖後就是最長路徑(每條邊的權重都是1),可以看出上圖其中一條最長路徑是1→3→5→end (看不出來的話是因為我畫的太醜)。 4. 上課時有說,要找DAG的最長路徑,只要把邊的權重改成負的(或是把小於改大於),然後找最短路徑就好。因為只有一個起點的最短路徑比較好算,所以把邊反向,然後把end當成起點。 5. 用LIS回推LCS。 ## Pseudo Code ### 步驟1 ```python= # R為字元編碼的範圍 p[R] = Null # 為了進一步縮短時間,所以不使用Counting Sort,直接random access d[] # list adj[] # dictionary input s1[], s2[] if s1.length > s2.length: swap(s1, s2) # 因為2nd components是遞減的,所以倒過來看 for i from s1.length to 1: p[decode(s1[i])].append(i) # s為合併後的數列 for c in s2: if p[decode(c)] == Null: continue for i in p[decode(c)]: s.append(i) # 建立DAG,應該有更快的方法,但我不知道 for i from 1 to s.length: adj.append(s[i]) adj[s[i]].append(end) for j from i + 1 to s.length: if s[j] > s[i]: adj[s[i]].append(s[j]) s.append(end) # 反向 adj_T[] for l in adj: for v in l: adj_T[v].append(l.begin) adj = adj_T ``` ### 步驟2 Type I ```python= v = s.length π[v] = Null, d[v] = -∞ # create list d[end] = 0 for u in s.reverse: for v in adj[u]: if d[v] < d[u] + 1: d[v] = d[u] + 1 π[v] = u result[] # LIS v = argmax(d) while(v != end): result.append(v) v = π[v] ``` ### 步驟2 Type II ??? (應該不是這樣,我只是反過來做而已) ```python= v = s.length π[v] = Null, d[v] = 0 # create list for u in s: for v in adj[u]: if d[u] < d[v] + 1: d[u] = d[v] + 1 π[u] = v result[] # LIS v = π[end] while(v != Null): result.append(v) v = π[v] result = result.reverse ``` ### 步驟3 ```python= # 用LIS回推LCS for i in result: print s1[i] ``` ## Analysis * 假設s1的長度為m,s2的長度為n,且m ≤ n。 * 時間複雜度: 跟建adjacency list的時間有關,我用的方法是$\Theta(m^2),應該還能再更快$