Location via proxy:   [ UP ]  
[Report a bug]   [Manage cookies]                
0% found this document useful (0 votes)
0 views

Levenshtein Distance - Coderust_ Hacking the Coding Interview

The document discusses the Levenshtein distance, a metric for measuring the difference between two strings, and provides a detailed explanation of the coin change problem using dynamic programming. It includes hints, a coding exercise, and solutions with their respective runtime and memory complexities. The document also presents an optimized iterative solution to the Levenshtein distance calculation, reducing memory complexity while maintaining a quadratic runtime complexity.

Uploaded by

AshishManchanda
Copyright
© © All Rights Reserved
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
0 views

Levenshtein Distance - Coderust_ Hacking the Coding Interview

The document discusses the Levenshtein distance, a metric for measuring the difference between two strings, and provides a detailed explanation of the coin change problem using dynamic programming. It includes hints, a coding exercise, and solutions with their respective runtime and memory complexities. The document also presents an optimized iterative solution to the Levenshtein distance calculation, reducing memory complexity while maintaining a quadratic runtime complexity.

Uploaded by

AshishManchanda
Copyright
© © All Rights Reserved
Available Formats
Download as PDF, TXT or read online on Scribd
You are on page 1/ 10

02/11/2020 Levenshtein Distance - Coderust: Hacking the Coding Interview

Levenshtein Distance
Compute the Levenshtein distance between two strings.

We'll cover the following

• Description
• Hints
• Try it yourself
• Solution
• Runtime complexity
• Memory complexity
• Solution 3: iterative (optimized)
• Runtime complexity
• Memory complexity

Description #
Suppose we have coin denominations of [1, 2, 5] and the total amount is 7.
We can make changes in the following 6 ways:

https://www.educative.io/courses/coderust-hacking-the-coding-interview/j2A0v 1/10
02/11/2020 Levenshtein Distance - Coderust: Hacking the Coding Interview

Denominations 1, 2, 5
Amount 7

No. of ways to make the change


1, 1, 1, 1, 1, 1, 1
1, 1, 1, 1, 1, 2
1, 1, 1, 2, 2
1, 2, 2, 2
1, 1, 5
2, 5

Total Methods 6

Hints #
Use principles of dynamic programming.

Try it yourself #
C++ Java Python JS Ruby

int solve_coin_change(vector<int>& denominations, int amount) {


// TODO: Write - Your - Code
return -1;
}

Solution #
Runtime complexity #
The runtime complexity of this solution is quadratic, O(m × n), where m
is the number of denominations and n is the total amount.

Memory complexity #

https://www.educative.io/courses/coderust-hacking-the-coding-interview/j2A0v 2/10
02/11/2020 Levenshtein Distance - Coderust: Hacking the Coding Interview

The memory complexity of this solution is linear, O(n), where n is the


total amount.

To apply a dynamic programming approach, the problem to be solved


must have optimal substructure. Optimal structure means that the
optimal solution of the problem at hand could be achieved by leveraging
the optimal solutions of its subproblems and overlapping sub-problems.
One way to achieve this is by using pre-computed values for solved sub-
problems so that we don’t have to solve them again.

The coin changing problem has both optimal substructure, meaning that it
can be easily divided to simpler problems and they can be solved to find
the final solution. It also satisfies the property of overlapping sub
problems, meaning previously solved sub problem results can be used
multiple times.

To solve this problem, we’ll keep an array of size amount + 1 . One


additional space is reserved because we also want to store the solution for
the 0 amount.

There is only one way you can make a change of 0 , i.e., select no coin so
we’ll initialize solution[0] = 1 . We’ll solve the problem for each
amount, denomination to amount, using coins up to a denomination, den .

The results of different denominations should be stored in the array


solution .

The solution for amount x using a denomination den will then be:

solution[x] = solution[x] + solution[x - den]

We’ll repeat this process for all the denominations, and at the last element
of the solution array, we will have the solution.

Considering the example mentioned above, the code works in the


following way:

https://www.educative.io/courses/coderust-hacking-the-coding-interview/j2A0v 3/10
02/11/2020 Levenshtein Distance - Coderust: Hacking the Coding Interview

Initially solution[0] = 1 and solution[x] = 0 for all x > 0 and x


<= amount .

We’ll start with first denomination which is 1 in our example, and


will update solution[x] = solution[x] + solution[x - 1] for all
1 <= x <= amount .

Now, we’ll use second denomination, i.e. 2 , in our example. We’ll


compute solution[x] = solution[x] + solution[x - 2] for all 2
<= x <= amount .

At the end, we’ll use third denomination, i.e. 5 , in our example. We’ll
compute solution[x] = solution[x] + solution[x - 5] for all 5
<= x <= amount .

Solution array initialized


Denominations 1, 2, 5
Amount 7

0 0 0 0 0 0 0 0

1 of 19

C++ Java Python JS Ruby

https://www.educative.io/courses/coderust-hacking-the-coding-interview/j2A0v 4/10
02/11/2020 Levenshtein Distance - Coderust: Hacking the Coding Interview

int solve_coin_change(vector<int>& denominations, int amount) {

vector<int> solution(amount + 1);


solution[0] = 1;

for (int den: denominations) {


for (int i = den; i < (amount + 1); ++i) {
solution[i] += solution[i - den];
}
}

return solution.back();
}

int main() {
vector<int> denominations = {1, 2, 5};
int amount = 7;
int result = solve_coin_change(denominations, amount);
// printing result
cout<< "solve_coin_change_dp([";
for (int den: denominations){
cout << den << " ";
}
cout << "], " << amount << ") = " << result;
}

C++ Java Python JS Ruby

https://www.educative.io/courses/coderust-hacking-the-coding-interview/j2A0v 5/10
02/11/2020 Levenshtein Distance - Coderust: Hacking the Coding Interview

int minimum(int a, int b, int c) {


return min(min(a, b), c);
}

int levenshtein_distance(string str1, string str2) {


//degenerate cases
if (str1 == str2)
return 0;

if (str1.length() == 0)
return str2.length();

if (str2.length() == 0)
return str1.length();

// for all i and j, d[i,j] will hold the Levenshtein distance between
// the first i characters of str1 and the first j characters of str2;
// note that d has (m+1)*(n+1) values
int d[str1.length() + 1][str2.length() + 1];

// source prefixes can be transformed into empty string by


// dropping all characters
for (int i = 0; i <= str1.length(); i++) {
d[i][0] = i;
}

// target prefixes can be reached from empty source prefix


// by inserting every character
for (int j = 1; j <= str2.length(); j++) {
d[0][j] = j;
}

int cost;
for (int i = 1; i <= str1.length(); i++) {
for (int j = 1; j <= str2.length(); j++) {

if (str1.at(i - 1) == str2.at(j - 1))


cost = 0; // no operation required
else
cost = 1;

d[i][j] = minimum(
d[i - 1][j] + 1, // a deletion
d[i][j - 1] + 1, // an insertion
d[i - 1][j - 1] + cost); // a substitution
}
}

return d[str1.length()][str2.length()];
}

int main() {

string str1 = "kitten";


string str2 = "sitting";
int ld = levenshtein_distance(str1, str2);

https://www.educative.io/courses/coderust-hacking-the-coding-interview/j2A0v 6/10
02/11/2020 Levenshtein Distance - Coderust: Hacking the Coding Interview
cout << "LD(" << str1 << ", " << str2 << ") = " << ld;
}

Solution 3: iterative (optimized) #


Runtime complexity #
The runtime complexity of this solution is quadratic, O(m × n), where m
and n are lengths of the input strings.

Memory complexity #
The runtime complexity of this solution is linear, O(n), where m and n
are lengths of the input strings.

Solution 3 uses a similar approach to solution 2, but it reduces the


memory complexity to O(n). Only two rows of the two-dimensional array
are required if we do not want to reconstruct the edited input strings: the
previous row and the current row being calculated.

The optimized iterative algorithm with two rows for strings s1 = kitten
and s2 = sitting is as follows:

https://www.educative.io/courses/coderust-hacking-the-coding-interview/j2A0v 7/10
02/11/2020 Levenshtein Distance - Coderust: Hacking the Coding Interview

if s1 is equal to s2,
return 0

set m as length of s1
set n as length of s2

if s1 is empty,
return n
if s2 is empty,
return m

create two arrays of integer distances d1[] and d2[] of length n


+1
initialize d1 (previous row of distances) from 0..n
for each i from 0..m
calculate d2 (current row of distances) from the previous ro
w d1 as follows:
d2[0] = i + 1

for each j from 0..n


if s1[i] is equal to s2[j], initialize cost to 0, otherwis
e initialize cost to 1
d2[j+1] = minimum( d2[j] +1, d1[j+1] + 1, d1[j] + cost )

copy d2 (current row) to d1 (previous row) for next iteration

return d2[n]

Let’s understand the iterative algorithm with the illustrations below:

s1 = kitten, m=6
s2 = sitting, n=7 0 1 2 3 4 5 6 7
d1
Create two arrays
d1[] and d2[] of
length n+1,
n=7 in this case 0 1 2 3 4 5 6 7
d2

1 of 13

https://www.educative.io/courses/coderust-hacking-the-coding-interview/j2A0v 8/10
02/11/2020 Levenshtein Distance - Coderust: Hacking the Coding Interview

C++ Java Python JS Ruby

https://www.educative.io/courses/coderust-hacking-the-coding-interview/j2A0v 9/10
02/11/2020 Levenshtein Distance - Coderust: Hacking the Coding Interview

int minimum(int a, int b, int c) {


return min(min(a, b), c);
}

int levenshtein_distance(string str1, string str2) {


//degenerate cases
if (str1 == str2)
return 0;

if (str1.length() == 0)
return str2.length();

if (str2.length() == 0)
return str1.length();

// create two work arrays of integer distances


int d1[str2.length() + 1];
int d2[str2.length() + 1];

// initialize d1 (the previous row of distances)


// this row is A[0][i]: edit distance for an empty str1
// the distance is just the number of characters to delete from str2
for (int i = 0; i <= str2.length(); i++)
d1[i] = i;

int cost;
for (int i = 0; i < str1.length(); i++) {

// calculate d2 (current row distances) from the previous row d1

// first element of d2 is A[i+1][0]


// edit distance is delete (i+1) chars from str1 to match empty str2
d2[0] = i + 1;

// use formula to fill in the rest of the row


for (int j = 0; j < str2.length(); j++) {

if (str1.at(i) == str2.at(j))
cost = 0;
else

https://www.educative.io/courses/coderust-hacking-the-coding-interview/j2A0v 10/10

You might also like