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