[LeetCode#161] One Edit Distance

Problem:

Given two strings S and T, determine if they are both one edit distance apart.

General Analysis:

This problem is not hard. However, to write out more efficient and elegant solution, we need to dive more deep to understand the logic behind it. 

Instant idea:
1. The length difference betweeen string "s" and string "t" must not longer than 1, otherwise, no one edit operation could make them same. (at least two insert or two delete actions)
2. For it is valid, it could be following cases:
2.1 s.length() == t.length() 
s and t have only one character in difference.

2.2 s.length() == t.length() - 1 (s.length() < t.length())
a. insert one character into s. 
b. append one charcter after s. 

Note: append one charcter after s is strange, it could easily break your solution!

Wrong Solution 1:

public class Solution {
    public boolean isOneEditDistance(String s, String t) {
        int s_len = s.length();
        int t_len = t.length();
        if (s_len == 0 && t_len == 0)
            return false;
        if (Math.abs(s_len - t_len) > 1) {
            return false;
        } else if(Math.abs(s_len - t_len) == 1) {
            String longer = s;
            String shorter = t;
            if (s_len < t_len) {
                longer = t;
                shorter = s;
            }
            for (int i = 0; i < longer.length(); i++) {
                String new_longer = longer.substring(0, i) + longer.substring(i+1);
                if (new_longer.equals(shorter))
                    return true;
            }
        } else{
            int diff_count = 0;
            for (int i = 0; i < s_len; i++) {
                if (s.charAt(i) != t.charAt(i))
                    diff_count++;
            }
            if (diff_count != 1)
                return false;
        }
        return true;
    }
}

Mistake Analysis:

Input:
"", ""
Output:
true
Expected:
false

Mistakes:
The above solution must be wrong!!! How could you mixed "return false" and "return true" in the middle of code! It could easily break your logic. 

Inefficient Solution 1:

public class Solution {
    public boolean isOneEditDistance(String s, String t) {
        int s_len = s.length();
        int t_len = t.length();
        if (s_len == 0 && t_len == 0)
            return false;
        if (Math.abs(s_len - t_len) > 1) {
            return false;
        } else if(Math.abs(s_len - t_len) == 1) {
            String longer = s;
            String shorter = t;
            if (s_len < t_len) {
                longer = t;
                shorter = s;
            }
            for (int i = 0; i < longer.length(); i++) {
                String new_longer = longer.substring(0, i) + longer.substring(i+1);
                if (new_longer.equals(shorter))
                    return true;
            }
        } else{
            int diff_count = 0;
            for (int i = 0; i < s_len; i++) {
                if (s.charAt(i) != t.charAt(i))
                    diff_count++;
            }
            if (diff_count == 1)
                return true;
        }
        return false;
    }
}

Analysis:

Even though the above solution was accepted, it is quite slow. Cause for the case of "s.length() == t.length() - 1", we have tried to delete each character in the t. which actually is quite uncessary!!!! 

What's more, I have a ugly code to assgin the longer and shorter string.
--------------------------------------------------------------
String longer = s;
String shorter = t;
if (s_len < t_len) {
    longer = t;
    shorter = s;
}
---------------------------------------------------------------
Which could be smartly written as 
if (t_len < s_len)
    return isOneEditDistance(t, s);
It means s is always the shorter String. 


Improve analysis:
Actually the above solution is really really ugly, we consturct a new string and make the compare every time. The efficiency is unacceptable!

There actually could be a more easy way to do it!
We know we actually could only do two things to make two string equal, one is to insert a new character in to the shorter string s or replace one character in s. And the two actions were only allowed to perform once!

Basic idea:
We compare the characters in s and t one by one. Once we detect a difference, we try to use following two ways to fix it. 
1. insert the difference character into s.
String inserted = (s.substring(0, i) + t.charAt(i) + s.substring(i));
2. reaplce the character in s.
String replaced = (s.substring(0, i) + t.charAt(i) + s.substring(i+1));   

If one of them works, it means we could use only one edit to make those two string equal. However if both of them fail, we should return false!!!<Since we only allowed to do one edit, and the conflict at current position could not be simply solved by one edit>

In the case of there is no difference in common length part of s and t, we should also distinguish following two cases:
1. s == t, which should return false.
2. s == t + 1, which should return true, since we could append one character after s to make them same.

while (i < s_len && j < t_len) {
    if (s.charAt(i) != t.charAt(j)) {
        ...
        return replaced.equals(t) || inserted.equals(t);
    }
...
return s_len + 1 == t_len;

Efficient Solution 1:

public class Solution {
    public boolean isOneEditDistance(String s, String t) {
        int s_len = s.length();
        int t_len = t.length();
        if (t_len < s_len)
            return isOneEditDistance(t, s);
        if (t_len - s_len > 1)
            return false;
        int i = 0;
        int j = 0;
        while (i < s_len && j < t_len) {
            if (s.charAt(i) != t.charAt(j)) {
                String replaced = (s.substring(0, i) + t.charAt(i) + s.substring(i+1));   
                String inserted = (s.substring(0, i) + t.charAt(i) + s.substring(i));
                return replaced.equals(t) || inserted.equals(t);
            }
            i++;
            j++;
        }
        return s_len + 1 == t_len;
    }
}

Improve Analysis:

The aove solution could be written in a more elegant way. 
1. use only one index i, since until we return the result, i and j share the same start and pace.
int i = 0;
int j = 0;
while (i < s_len && j < t_len) {
    ...
    i++;
    j++;
}

2. no need to compare the suppposed common part among s and t.
String replaced = (s.substring(0, i) + t.charAt(i) + s.substring(i+1));   
String inserted = (s.substring(0, i) + t.charAt(i) + s.substring(i));
return replaced.equals(t) || inserted.equals(t);

Since s.substring(0, i) is common is s and t. any after the edits, t.charAt(i) was inserted/replaced into s. We absolutely have no need to compare them again.
if (s.charAt(i) != t.charAt(i))
    return s.substring(i+1).equals(t.substring(i+1)) || s.substring(i).equals(t.substring(i+1));

Upgraded Solution:

public class Solution {
    public boolean isOneEditDistance(String s, String t) {
        int s_len = s.length();
        int t_len = t.length();
        if (t_len < s_len)
            return isOneEditDistance(t, s);
        if (t_len - s_len > 1)
            return false;
        int i = 0;
        while (i < s_len) {
            if (s.charAt(i) != t.charAt(i))
                return s.substring(i+1).equals(t.substring(i+1)) || s.substring(i).equals(t.substring(i+1));
            i++;
        }
        return s_len + 1 == t_len;
    }
}
posted @ 2015-09-17 08:58  airforce  阅读(177)  评论(0编辑  收藏  举报