tgs returns the pairs of indexes of the longest common subsequence of unique lines in x and y, where a unique line is one that appears once in x and once in y. The longest common subsequence algorithm is as described in Thomas G. Szymanski, “A Special Case of the Maximal Common Subsequence Problem,
(x, y []string)
| 187 | // Subsequence Problem,” Princeton TR #170 (January 1975), |
| 188 | // available at https://research.swtch.com/tgs170.pdf. |
| 189 | func tgs(x, y []string) []pair { |
| 190 | // Count the number of times each string appears in a and b. |
| 191 | // We only care about 0, 1, many, counted as 0, -1, -2 |
| 192 | // for the x side and 0, -4, -8 for the y side. |
| 193 | // Using negative numbers now lets us distinguish positive line numbers later. |
| 194 | m := make(map[string]int) |
| 195 | for _, s := range x { |
| 196 | if c := m[s]; c > -2 { |
| 197 | m[s] = c - 1 |
| 198 | } |
| 199 | } |
| 200 | for _, s := range y { |
| 201 | if c := m[s]; c > -8 { |
| 202 | m[s] = c - 4 |
| 203 | } |
| 204 | } |
| 205 | |
| 206 | // Now unique strings can be identified by m[s] = -1+-4. |
| 207 | // |
| 208 | // Gather the indexes of those strings in x and y, building: |
| 209 | // xi[i] = increasing indexes of unique strings in x. |
| 210 | // yi[i] = increasing indexes of unique strings in y. |
| 211 | // inv[i] = index j such that x[xi[i]] = y[yi[j]]. |
| 212 | var xi, yi, inv []int |
| 213 | for i, s := range y { |
| 214 | if m[s] == -1+-4 { |
| 215 | m[s] = len(yi) |
| 216 | yi = append(yi, i) |
| 217 | } |
| 218 | } |
| 219 | for i, s := range x { |
| 220 | if j, ok := m[s]; ok && j >= 0 { |
| 221 | xi = append(xi, i) |
| 222 | inv = append(inv, j) |
| 223 | } |
| 224 | } |
| 225 | |
| 226 | // Apply Algorithm A from Szymanski's paper. |
| 227 | // In those terms, A = J = inv and B = [0, n). |
| 228 | // We add sentinel pairs {0,0}, and {len(x),len(y)} |
| 229 | // to the returned sequence, to help the processing loop. |
| 230 | J := inv |
| 231 | n := len(xi) |
| 232 | T := make([]int, n) |
| 233 | L := make([]int, n) |
| 234 | for i := range T { |
| 235 | T[i] = n + 1 |
| 236 | } |
| 237 | for i := 0; i < n; i++ { |
| 238 | k := sort.Search(n, func(k int) bool { |
| 239 | return T[k] >= J[i] |
| 240 | }) |
| 241 | T[k] = J[i] |
| 242 | L[i] = k + 1 |
| 243 | } |
| 244 | k := 0 |
| 245 | for _, v := range L { |
| 246 | if k < v { |