给定两个字符串,找出它们是否相互远离一个编辑

时间:2015-09-07 05:09:03

标签: string algorithm edit-distance

我最近遇到了这个问题:

Given two strings, return true if they are one edit away from each other,else return false.
An edit is insert/replace/delete a character. 
Ex. {"abc","ab"}->true, {"abc","adc"}->true, {"abc","cab"}->false

解决此问题的一种方法是使用动态编程找到两个字符串之间的编辑距离,并检查它是否为1.这将花费O(N2)时间。有没有办法在线性时间内完成此操作,因为我们只检查它们是否是1次编辑?

我在下面写的代码适用于大多数情况,但是对于像{" m",""}

这样的情况却失败了
public boolean isOneEditDistance(String s1,String s2){
    if(s1==null || s2==null)
        return true;
    if(s1.equals(s2))
        return false;
    if (Math.abs(s1.length() - s2.length()) > 1)
        return false;
    int i = -1;
    int j = -1;
    while (true)
    {
        i++;
        j++;
        if (i == s1.length())
            return true;
        if (s1.charAt(i) == s2.charAt(j))
            continue;
        if (i != j)
            return false;
        if (s1.length() < s2.length())
            i--;
        else
            j--;
    }
}

18 个答案:

答案 0 :(得分:7)

这是在O(n)中找到一个编辑的解决方案。以下是我已在实施中介绍的方案。

  1. 两个输入字符串之间的长度差异不应超过1.
  2. 当字符串的长度相同时,不同字符的数量不应超过1。
  3. 如果长度差异为1,则可以在短字符串中插入一个字符或从较长字符串中删除。考虑到这一点,不同char的数量不应超过1.
  4. private static boolean isOneEdit(String first, String second) {
        // if the input string are same
        if (first.equals(second))
            return false;
    
        int len1 = first.length();
        int len2 = second.length();
        // If the length difference of the stings is more than 1, return false.
        if ((len1 - len2) > 1 || (len2 - len1) > 1  ) {
            return false;
        }
        int i = 0, j = 0;
        int diff = 0;
        while (i<len1 && j<len2) {
            char f = first.charAt(i);
            char s = second.charAt(j);
            if (f != s) {
                diff++;
                if (len1 > len2)
                    i++;
                if (len2 > len1)
                    j++;
                if (len1 == len2)
                    i++; j++;
            }
            else{
                i++; j++;
            }
            if (diff > 1) {
                return false;
            }
        }
        // If the length of the string is not same. ex. "abc" and "abde" are not one edit distance.
        if (diff == 1 && len1 != len2 && (i != len1 || j != len2)) {
            return false;
        }
        return true;
    }
    

答案 1 :(得分:3)

在动态编程方法中,经常使用矩阵。行对应一个字符串,列对应另一个字符串。重点是找到从左上角单元到右下角最便宜的路径。在任何时候,水平或垂直过渡都代表插入。

您的问题是一样的,但路径受到限制。使用 k 插入/删除时,路径被限制在 k - 对角线中。除此之外,经典的DP算法应该可行。复杂性是线性的。

答案 2 :(得分:1)

我在 C ++ 中解决了该问题,它是Khalid Habib试图在this答案中说的正确版本。这是下面的解决方案(我也在Github上添加了该解决方案,您可以点击链接here)。

#include<bits/stdc++.h>
using namespace std;

// checks if there is only one one different in both arrays
bool oneReplaceAway(string s1, string s2){
    bool firstChangeDone = false;
    int l1 = s1.length();
    // l1 == l2 already
    // int l2 = s2.length();
    int l2 = l1;
    int i=0, j=0;

    while(i<l1 && j<l2){
        if(s1[i] != s2[j]){
            // if first change is already checked then return false as there are more than one dissimilarities
            if(firstChangeDone){
                //cout<<"IGI@"<< i<<" "<<j<<"\n";
                return false;   
            }
            firstChangeDone = true;
        }
        i++;
        j++;
    }
    return true;
}


// checks if one word can be added to one string to create the other one
bool oneInsertAway(string s1, string s2){
    bool firstChangeDone = false;
    int l1 = s1.length();
    int l2 = s2.length();

    int i=0, j=0;

    while(i<l1 && j<l2){
        if(s1[i]!=s2[j]){
            // if the chars at i and j are not equal and i!=j, that means one change is already checked, i.e., it is the second change
            if(i!=j)
                return false;
            j++;
        }
        else{
            i++;
            j++;
        }
    }
    return true;
}

// checks of two strings are One Edit Away
bool oneEditAway(string s1, string s2) {
    int l1 = s1.length();
    int l2 = s2.length();

    // cout<<s1<<" - "<<l1<<"\n"<<s2<<" - "<<l2<<"\n"<<(l1==l2)<<"\n";
    if(l1 == l2){
        return oneReplaceAway(s1, s2);
    }
    else if(l1+1 == l2){
        return oneInsertAway(s1, s2);
    }
    else if(l2+1 == l1){
        return oneInsertAway(s2, s1);
    }
    else
        return false;
}


int main(){
    int t;
    cin>>t;

    while(t--){
        string s1,s2;
        cin>>s1>>s2;

        // cout<<oneEditAway(s1, s2)<<"\n";
        string ans = oneEditAway(s1, s2)==1?"One Edit Awway":"Not one Edit Away";
        cout<<ans<<"\n";
    }
    return 0;
}

答案 3 :(得分:1)

在C#中有一种简单的方法。

    static bool OneEdit(string s1, string s2)
    {
        var diff = s1.Length > s2.Length
                ? s1.Except(s2).ToList()
                : s2.Except(s1).ToList();

        return diff.Count() == 1;
    }

答案 4 :(得分:1)

static boolean isEditDistanceOne(String s1, String s2)
    {
        // Find lengths of given strings
        int m = s1.length(), n = s2.length();

        // If difference between lengths is more than
        // 1, then strings can't be at one distance
        if (Math.abs(m - n) > 1)
            return false;

        int count = 0; // Count of edits

        int i = 0, j = 0;
        while (i < m && j < n)
        {
            // If current characters don't match
            if (s1.charAt(i) != s2.charAt(j))
            {
                if (count == 1)return false;

                // If length of one string is
                // more, then only possible edit
                // is to remove a character
                if (m > n) i++;
                else if (m< n) j++;
                else //Iflengths of both strings is same
                {
                    i++;
                    j++;
                }

                // Increment count of edits 
                count++;
            }

            else // If current characters match
            {
                i++;
                j++;
            }
        }

        // If last character is extra in any string
        if (i < m || j < n)
            count++;

        return count == 1;
    }

答案 5 :(得分:0)

    boolean oneEditAway(String first, String second) {
    if (first.length() == second.length()) {
        //call a function which replce the character from the string
    } else if (first.length() + 1 == second.length()) {
        //call a function which remove the character from string
    } else if (first.length() - 1 == second.length()) {
        //call a function which insert the character in the string
    }
    return false;
}

答案 6 :(得分:0)

这是Ruby实现:

def one_away(s1, s2)
  return false if (s1.size - s2.size).abs > 1

  missed_count = 0
  counter = 0
  s1.each_char do |c|
    if !s2[counter].nil? && (s2[counter] != c)
      missed_count += 1
    end
    counter += 1
    return false if missed_count > 1
  end
  true
end

p one_away('pale', 'bake') #=> false

答案 7 :(得分:0)

逐步解释 Swift 中的答案:

func isOneEdit(str1: String, str2: String) -> Bool {

// check if they are the same
if str1 == str2 {
    return true
}

let difference = abs(str1.count - str2.count)

// check if the difference between then is bigger than 1
if difference > 1 {
    return false
}

// lets iterate over the words

var i = 0
var j = 0
var changes = 0

while i < str1.count && j < str2.count {
    let char1 = str1[str1.index(str1.startIndex, offsetBy: i)]
    let char2 = str2[str1.index(str2.startIndex, offsetBy: j)]

    // if the difference is 1 we need to move just one index (the one from the bigger word)
    // this is just necessary when the char1 and char2 are different
    if difference == 1 && char1 != char2 {
        if str1.count > str2.count {
            i += 1
        } else {
            j += 1
        }
        changes += 1
    } else {
        // if chars are equal (in this step we don't care about the difference)
        // we move both indexes.
        i += 1
        j += 1

        if char1 != char2 {
            changes += 1
        }
    }
}

return changes <= 1
}

答案 8 :(得分:0)

Java版本可能如下:

public static boolean oneEdit(String w1, String w2) 
{
    char[] word1= w1.trim().toCharArray();
    char[] word2 = w2.trim().toCharArray();

    int length1=word1.length;
    int length2=word2.length;

    if(Math.abs(length2-length1) > 1) return false;

    Arrays.sort(word1);
    Arrays.sort(word2);

    int length = word1.length >= word2.length? word2.length:word1.length; //take the minimum length

    int falseCounter=0;
    for(int i=0; i < length; i++ ) {
        if(word1[i] != word2[i] && ++falseCounter > 1){
            return false;
        }
    }
    return true;
}

答案 9 :(得分:0)

C#版本

static bool IsOneEditAway(string word1, string word2)
        {
            if (string.IsNullOrEmpty(word1) && string.IsNullOrEmpty(word2))
                return false;

            ActionType actionToPerform;
            if (word1.Length == word2.Length)
            {
                actionToPerform = ActionType.Replace;
            }
            else if (word1.Length < word2.Length)
            {
                actionToPerform = ActionType.Delete;
            }
            else
                actionToPerform = ActionType.Insert;

            int i = 0, j = 0;
            var numOfEdits = 0;
            var chrWord1 = word1.ToCharArray();
            var chrWord2 = word2.ToCharArray();
            while (numOfEdits <= 1)
            {
                if (i >= chrWord1.Length && j >= chrWord2.Length)
                    break;
                if (i >= chrWord1.Length && j < chrWord2.Length)
                {
                    j++;
                    numOfEdits++;
                    continue;
                }
                if (j >= chrWord2.Length && i < chrWord1.Length)
                {
                    i++;
                    numOfEdits++;
                    continue;
                }
                if (chrWord1[i] == chrWord2[j])
                {
                    i++; j++;
                }
                else
                {
                    switch(actionToPerform)
                    {
                        case ActionType.Insert:
                            i++;
                            break;
                        case ActionType.Delete:
                            j++;
                            break;
                        case ActionType.Replace:
                            i++;j++;
                            break;
                    }
                    numOfEdits++;
                }
            }

            return numOfEdits == 1 ? true : false;
        }
public enum ActionType
    {
        Insert=0,
        Delete=1,
        Replace=2
    }

答案 10 :(得分:0)

这是我在C#的O(n)时间中的解决方案。几种情况:

  • 如果字符串长度的差大于1,则退出
  • 首先遍历第一个字符串,并为每个对应的字符递增英语小写数组
  • 然后遍历第二个字符串并递减相应的字符。
  • 最后,检查编辑计数是否大于1 ...如果是,请中断for循环...
  • 我们将仅考虑小写英文字母

    public static bool IsStringOneAway(string s1, string s2)
    {
        //we will consider lower-case English alphabets only
        int[] engArray = new int[26];
        var tmp = 0;
        var editCount = 0;
    
        //if string lengths differ by more than 1, then return
        if (Math.Abs(s1.Length - s2.Length) > 1)
        {
            Console.WriteLine("Length difference is more than 1, One Away edit doesn't exist");
            return false;
        }
    
        //append the english alphabet array from String 1
        for (int i = 0; i < s1.Length; i++)
        {
            tmp = (int)s1[i];
            engArray[tmp - 97]++;
        }
    
        //deduct the english alphabet arry from String 2
        for (int i = 0; i < s2.Length; i++)
        {
            tmp = (int)s2[i];
            engArray[tmp - 97]--;
        }
    
        //now check the count of edits; if count > 1, break
        for (int i = 0; i < engArray.Length; i++)
        {
            if (engArray[i] != 0)
            {
                editCount++;
                if (editCount > 2)
                {
                    Console.WriteLine("One Away edit doesn't exist");
                    return false;
                }
            }
        }
    
        Console.WriteLine("One Away edit exist");
        return true;
    
    }
    

答案 11 :(得分:0)

这是我的python实现。我为每个字符串使用两个数组

 import unittest

# Assume characters stored in 8 bits. 256 possible characters
MAX_CHARS = 256

def is_edit(string1, string2):
    """Given two strings, return if they are one or zero edits away.

    Insert, remove or replace a character."""
    # If the absolute difference in length is more than one
    # return false
    string1_len = len(string1)
    string2_len = len(string2)

    if string1_len != string2_len and abs(string1_len - string2_len) > 1:
        return False

    # Initialize two arrays, each for each string
    count1 = [0] * MAX_CHARS
    count2 = [0] * MAX_CHARS

    # For each character in input strings get unicode representation
    # and  increment counter in corresponding array
    for i in string1:
        count1[ord(i)] += 1

    for i in string2:
        count2[ord(i)] += 1

    edit = 0

    # compare the arrays
    # If number of edits required is more than 2 return false
    # This will handle replacement when given words of the same length
    for i in range(MAX_CHARS):
        if count1[i] != count2[i]:
            edit += 1

        if edit > 2:
            return False

    # Return false if string1 is the same as string2 (No edit required) e.g pale, pale
    if not edit:
        return False

    return True


class EditCheckTestCase(unittest.TestCase):
    """Tests for is_edit method."""

    def test_insert_missing_character(self):
        """Test insertion of character is valid."""
        self.assertEqual(is_edit('pale', 'ple'), True)

    def test_insert_more_than_one_character(self):
        """Test insertion of more than one character is invalid"""
        self.assertEqual(is_edit('pale', 'pe'), False)

    def test_append_one_character(self):
        """Test the append of one character is valid."""
        self.assertEqual(is_edit('pales', 'pale'), True)

    def test_append_more_than_one_character(self):
        """Test append more than one character is invalid."""
        self.assertEqual(is_edit('paless', 'pale'), False)

    def test_replace_one_character(self):
        """Test replacement of one character is valid"""
        self.assertEqual(is_edit('pale', 'bale'), True)

    def test_no_edit_character(self):
        """Test no edit required is valid."""
        self.assertEqual(is_edit('pale', 'bake'), False)
        self.assertEqual(is_edit('pale', 'pale'), False)


if __name__ == "__main__":
    unittest.main()

答案 12 :(得分:0)

diameter = float(input("Enter a diameter: "))
print (diameter)
radius = (diameter/2)
print("The radius is equal to : ", radius)

答案 13 :(得分:0)

这将花费o(n)运行时间

  public static  boolean isOneEditDistance(String str1 ,String str2 ){
    if(Math.abs(str1.length() - str2.length()) > 1){
        return false;
    }
    String s1 = str1.length() < str2.length() ? str1 : str2; // smallest
    String s2 = str1.length() < str2.length() ? str2 : str1; // biggest
    int index1 = 0, index2 = 0;
    boolean isMoreThanOneEdit = false;

    while (index1 < s1.length() && index2 < s2.length()){
        if(s1.charAt(index1) != s2.charAt(index2)){
            if(isMoreThanOneEdit)
                return false;
            isMoreThanOneEdit = true;
            if(s1.length() == s2.length()) // if replace
                index1++;
        }else {
            index1++; // if match
        }
        index2++; // always longer string index
    }
    return true;
}

答案 14 :(得分:0)

C.this.type#T

答案 15 :(得分:0)

在这里,您可以在Swift中找到解决方案。

func isOneEdit(str1: String, str2: String) -> Bool {

  //The length difference between two input strings should not be more than 1.
  let diff = abs(str1.characters.count - str2.characters.count)
  if diff > 1 {
    return false
  }

  //When the length of the strings is same, the number of different chars should not be more than 1.
  if diff == 0 {
    var numberOfEdits = 0
    for (char1, char2) in zip(str1.characters, str2.characters) {
      if char1 != char2 {
        numberOfEdits++
      }
    }
    return numberOfEdits < 2
  }

  //If the length difference is 1.
  //then either one char can be inserted in the short string or deleted from the longer string. 
  //Considering that, the number of different char should not be more than 1.

  return str1.rangeOfString(str2) != nil || str2.rangeOfString(str1) != nil



  //return str1.isSubstring(str2) || str2.isSubstring(str1)

}

此函数需要O(n)时间和恒定空间。

答案 16 :(得分:0)

Java版本

public class Test {

public static void main(String[] args) {

    System.out.println(fun("car", "cart"));
    System.out.println(fun("cat", "bat"));
    System.out.println(fun("balck", "back"));
}

/*
 * Modifications : add, delete, update
 * 
 * i/p Example Add: a = car b = cart
 * 
 * delete : a = balck b = back
 * 
 * update: a = cat b = bat
 * 
 */
public static boolean fun(String a, String b) {
    Boolean isTestPositive = Boolean.FALSE;
    if (a == null || b == null) {
        return isTestPositive;
    }
    if (a.equals(b)) {
        // No Modifications required
        return isTestPositive;
    }
    // Start comparing
    char[] arrayForA = a.toCharArray();
    char[] arrayForB = b.toCharArray();

    return testCase(arrayForA, arrayForB);

}

public static boolean testCase(char[] a, char[] b) {
    int correctionCount = 0;
    int aLen = a.length;
    int bLen = b.length;
    if (Math.abs(aLen - bLen) > 1) {
        return Boolean.FALSE;
    }
    int minLen = Math.min(aLen, bLen);
    for (int i = 0; i < minLen; i++) {
        if (a[i] != b[i]) {
            ++correctionCount;
            if (correctionCount > 1) {
                return Boolean.FALSE;
            }
            // Test Delete case
            if (b.length > i + 1 && a[i] == b[i + 1]) {
                return testDeleteCase(Arrays.copyOfRange(a, i, a.length - 1),
                        Arrays.copyOfRange(b, i + 1, b.length - 1));
            } else if (a.length > i + 1 && b[i] == a[i + 1]) {
                return testDeleteCase(Arrays.copyOfRange(a, i + 1, a.length - 1),
                        Arrays.copyOfRange(b, i, b.length - 1));
            }

        }

    }
    return Boolean.TRUE;
}

public static boolean testDeleteCase(char[] a, char[] b) {
    int aLen = a.length;
    int bLen = b.length;
    if (Math.abs(aLen - bLen) >= 1) {
        return Boolean.FALSE;
    }
    int minLen = Math.min(aLen, bLen);
    for (int i = 0; i < minLen; i++) {
        if (a[i] != b[i]) {

            return Boolean.FALSE;
        }
    }
    return Boolean.TRUE;
}}

答案 17 :(得分:0)

Java 中的通用实现,在 O(N) 中运行所需的编辑次数。它确定两个字符串应该小于或等于 edits

public boolean isEditsAway(String s1, String s2, int edits) {
        int abs = Math.abs(s1.length() - s2.length());
        if (abs > edits)
            return false;

        int edit = 0;
        for (int i = 0; i < s1.length() && i < s2.length(); i++) {
            if (s1.charAt(i) != s2.charAt(i))
                edit++;
            if (edit == edits + 1)
                return false;
        }

        return abs + edit <= edits;
    }