SEATSR-Editorial

PROBLEM LINK:

Practice
Contest

Author: Sergey Nagin
Tester: Jingbo Shang
Editorialist: Ajay K. Verma

DIFFICULTY:

Easy-Medium

PREREQUISITES:

Dynamic programming, Edit Distance

PROBLEM:

Given two strings, and and integer k, determine if the edit distance between the two strings exceeds k, and if not, then also compute the edit distance between the two strings.

EXPLANATION:

We are given a string s, which we want to transform into another string w using the following three operations:

  1. add a character at any position , cost = a
  2. delete a character, cost = a
  3. replace a character, cost = b

The goal is to minimize the cost, and if the cost exceeds a given value k, then return -1. Note that if a = 0, then the answer will be zero, as we can remove insert/delete any character in the first string at zero cost. From now onwards, we will assume that a > 0.

This is a standard edit-distance problem, which can be solved using dynamic programming in O (N2) time, where N is the size of the string. However, in our case N is too large and the Wagner–Fischer algorithm of quadratic complexity will run out of time. However, there is a special constraint here, which is that we need to compute the edit distance only if it does not exceed k. This can be used to reduce the complexity to O (Nk).

In order to understand the modification, we first need to understand the Wagner–Fischer algorithm, which we highlight very briefly in the next section. If you are unaware of this algorithm, please take a moment to go through the above mentioned link.

Wagner–Fischer algorithm:

Suppose of the of string s is m, and the size of string w is n, then we create a two dimensional array d[0…m][0…n], where d[i][j] denotes the edit distance between the i-length prefix of s and j-length prefix of w.

The computation of array d is done using dynamic programming, which uses the following recurrence:
d[i][0] = i * a, for i <= m
d[0][j] = j * a, for j <= n

d[i][j] = d[i - 1][j - 1], if s[i] == w[j],
d[i][j] = min(d[i - 1][j] + a, d[i][j - 1] + a, d[i - 1][j - 1] + b), otherwise.

Restricted Edit Distance:

We can modify the definition of array d[][], a little bit.
d[i][j] = inf, if the edit distance between the i-length prefix of s and j-length prefix of w exceeds k,
d[i][j] = edit distance between the i-length prefix of s and j-length prefix of w, otherwise.

Note that if (i - j) > k, then we need to delete more than k characters from the first string, hence, the edit distance will be greater than k * a >= k. Similarly, if (i - j) < -k, then we need to add more than k characters in the first string, which will again have a cost greater than k * a >= k. Hence, d[i][j] = inf, if |i - j| > k

This means we only need to compute the values d[i][j], where |i - j| <= k. All other values in the array has to be infinite. There are only O(N * k) such entries, hence the complexity reduces to O (Nk).

For simplicity, we can use another array P[0…N][-k…k], such that
P[i][delta] = d[i][i + delta]
We are using negative indices only for simplicity, an offset can be used to make the indices non-negative.

The recurrence for P can be written easily using the recurrences for d.
P[i][- i] = i * a, for i <= k
P[0][i] = i * a, for i <= k

P[i][delta] = P[i - 1][delta], if s[i] == w[i + delta]
P[i][delta] = min(P[i - 1][delta + 1] + a, P[i][delta - 1] + a, P[i - 1][delta] + b), otherwise

In the recurrence, one should use the value inf, if delta goes outside the range [-k, k].

If the difference between the length of the two strings is more than k, then the answer will be -1, otherwise answer will be P[s.length()][w.length() - s.length()]. Once again, it should be checked if the final answer exceeds k, if so then we should return -1 instead.

Time Complexity:

O (Nk)

AUTHOR’S AND TESTER’S SOLUTIONS:

Author’s solution will be put up soon.
Tester’s solution will be put up soon.

26 Likes

Please note a correction in the editorial, for the statement (i-j) < k , it should be (i-j) < -k.

1 Like

thanks, corrected.

1 Like

Could be done by Ukkonen’s Algorithm:

27 Likes

What is wrong in this solution?

Solution

Great to fellow programmers referring to research papers, @kaushik_iiitd and yes (Y) for @sereja :slight_smile:

4 Likes

If no more than k differences should be found. In this case it is necessary to calculate only the diagonal band of width 2k+1 in matrix (Ukkonen cut-off), which reduces the time complexity to O (k min (m, n))

I think this figure is best to visualize the idea.

k step right and k step left to the main diagonal will do.

alt text

My Solution which implement this

6 Likes

higher quality PDF of the paper available here @ http://www.sciencedirect.com/science/article/pii/S0019995885800462

1 Like

I seem to have gotten most of the insights mentioned in this editorial, but my solution is still WA.

Any idea why? or possibly provide a test case where my algo would fail, so I can see where I went wrong, and what I’ve missed out?

Thanks

I referenced this diagram (@ http://i.stack.imgur.com/2LtnD.png) instead for my implementation (which turns out to be WA) is there a way to reconcile the two? meaning how should I setup the for-loops to iterate the correct bounds as necessary to compute the final answer?

I edited my ans with link of my solution, see if it helps.

I used this logic, but getting wrong answer. Can somebody plz help me finding where i was wrong?

//m is length(X)+1            
//n is length(Y)+1
//long long int arr[100005][2]
//flag is 0 or 1, initialized by 0

long long int DP()
{
        if(a==0)
           return 0;
    
        diff=m-n;
        if(diff<0)
          diff=(-diff);
    
        if(diff*a>k)
            return -1;
  
        if(b==0)
         {
             if(diff*a <=k)
                return diff*a;
             else
                return -1;
         }
       
        for(i=1;i<m;i++)
	{
		for(j=1;j<n;j++)
		{
                               //calculation for replacement	
				if(i==1 && j==1)
					corner=0;
				else if(i==1)
					corner=(j-1)*a;
				else if(j==1)
					corner=(i-1)*a;
				else
					corner=arr[1-flag][j-1];
    
            			if(X[i-1]!=Y[j-1])
					corner=corner+b;	
				
                                //calculation for addition			
				if(j==1)
					left=i*a;
				else
					left=arr[flag][j-1];
    
				left=left+a;

                               //calculation for deletion
				if(i==1)
					top=j*a;
				else
					top=arr[1-flag][j];
			
				top=top+a;
			
				arr[flag][j]=Minimum(left,top,corner);
			
		}
		flag=1-flag;
	}

			return arr[1-flag][n-1];

}

Why is this problem tagged as Easy-Medium in the editorial tag? I think it should be marked at least as Medium because it requires pre-requisite knowledge of a popular dynamic programming algorithm and then we need to optimize it further.

The reason why I am pointing this out is that several users who start off with competitive programming on Codechef, start solving problems from the Easy subpart in the Practice Section. It can be demotivating for a user if he/she is unable to solve the problem even though it is in the so-called “Easy” Section.

I guess it is very common that even some rather difficult problems are marked as Medium.

Of course, difficulty is relative, it differs from person to person. However, some problems like this one are definitely not Easy.

Do correct me if I am wrong :slight_smile:

18 Likes

I have to agree. I think the tags are a bit off this time.
ChefSquare = easy, SeaStr = easy-med and Qstring - med (considering O(log n) per query)

1 Like

alright, will take a look at it. thanks

Its in the medium section though @wittyceaser. So even if anybody practices he’ll find it in medium!

1 Like

Yup, I saw that but my intention was to address this issue on an overall basis and not just this problem.

1 Like

Could anyone point out what’s wrong with my solution? I’m getting a TLE. I used Ukkonen’s Algorithm for my approach. I’m using a single array to store the edit distances and a temporary variable to store the value of what would be d[i-1][j-1], if the O(N2) approach with the d[0…m][0…n] matrix is used. I seem to have missed something and I haven’t been able to figure it out yet.

what is delta in edtiorial

I’d like to ask:

How you generated some let say random inputs, but with known result to test your algorithm?

Because of small k I thought, that I can use this:

Both input string should be in form of

...S1...S2...S3...SN...

where S1, S2, … SN are some common substrings and “…” is some mess.

To find longest substring I wanted to use DP, which is unfortunately O(n2) (n is the length of longer string), so I decided I can try to find those in segments of 400 characters (if there is not common string result is -1).

When I did this I simply replaced S1, …, SN with characters that were not in initial strings and run “find longest common subsequence algorithm” also O(n2) algorithm, but now for string with length of ~200 characters…

Expected complexity something like 100400400 + 200*200 per test case, but I didn’t get to TLE, because it worked on my PC, but was failing for some test cases.