1
+ extension String {
2
+ func longestCommonSubsequence( other: String ) -> String {
3
+
4
+ // Computes the length of the lcs using dynamic programming
5
+ // Output is a matrix of size (n+1)x(m+1), where matrix[x][y] indicates the length
6
+ // of lcs between substring (0, x-1) of self and substring (0, y-1) of other.
7
+ func lcsLength( other: String ) -> [ [ Int ] ] {
8
+
9
+ //Matrix of size (n+1)x(m+1), algorithm needs first row and first column to be filled with 0
10
+ var matrix = [ [ Int] ] ( count: self . characters. count+ 1 , repeatedValue: [ Int] ( count: other. characters. count+ 1 , repeatedValue: 0 ) )
11
+
12
+ for (i, selfChar) in self . characters. enumerate ( ) {
13
+ for (j, otherChar) in other. characters. enumerate ( ) {
14
+ if ( otherChar == selfChar) {
15
+ //Common char found, add 1 to highest lcs found so far
16
+ matrix [ i+ 1 ] [ j+ 1 ] = ( matrix [ i] [ j] ) + 1
17
+ }
18
+ else {
19
+ //Not a match, propagates highest lcs length found so far
20
+ matrix [ i+ 1 ] [ j+ 1 ] = max ( matrix [ i] [ j+ 1 ] , matrix [ i+ 1 ] [ j] )
21
+ }
22
+ }
23
+ }
24
+
25
+ //Due to propagation, lcs length is at matrix[n][m]
26
+ return matrix;
27
+ }
28
+
29
+ //Backtracks from matrix[n][m] to matrix[1][1] looking for chars that are common to both strings
30
+ func backtrack( matrix: [ [ Int ] ] ) -> String {
31
+ var i = self . characters. count
32
+ var j = other. characters. count
33
+
34
+ //charInSequence is in sync with i so we can get self[i]
35
+ var charInSequence = self . endIndex
36
+
37
+ var lcs = String ( )
38
+
39
+ while ( i >= 1 && j >= 1 ) {
40
+ //Indicates propagation without change, i.e. no new char was added to lcs
41
+ if ( matrix [ i] [ j] == matrix [ i] [ j - 1 ] ) {
42
+ j = j - 1
43
+ }
44
+ //Indicates propagation without change, i.e. no new char was added to lcs
45
+ else if ( matrix [ i] [ j] == matrix [ i - 1 ] [ j] ) {
46
+ i = i - 1
47
+ //As i was subtracted, move back charInSequence
48
+ charInSequence = charInSequence. predecessor ( )
49
+ }
50
+ //Value on the left and above are different than current cell. This means 1 was added to lcs length (line 16)
51
+ else {
52
+ i = i - 1
53
+ j = j - 1
54
+ charInSequence = charInSequence. predecessor ( )
55
+
56
+ lcs. append ( self [ charInSequence] )
57
+ }
58
+ }
59
+
60
+ //Due to backtrack, chars were added in reverse order: reverse it back.
61
+ //Append and reverse is faster than inserting at index 0
62
+ return String ( lcs. characters. reverse ( ) ) ;
63
+ }
64
+
65
+ //Combine dynamic programming approach with backtrack to find the lcs
66
+ return backtrack ( lcsLength ( other) )
67
+ }
68
+ }
0 commit comments