【问题标题】:Edit Distance Java编辑距离 Java
【发布时间】:2018-04-30 01:23:42
【问题描述】:

我编写了这个算法来计算删除和插入(因此,编辑)数量的总和,以使第一个字符串等于第二个字符串。但它不起作用。

public static int distance (String s1, String s2) {
    return distance(s1, s2, 0, 0);
}

private static int distance(String s1, String s2, int i, int j) {
    if (i == s1.length) return j;
    if (j == s2.length) return i;
    if (s1.charAt(i) == s2.charAt(j))
        return distance(s1, s2, i + 1, j + 1);
    int rep = distance(s1, s2, i + 1, j + 1) + 1;
    int del = distance(s1, s2, i, j + 1) + 1;
    int ins = distance(s1, s2, i + 1, j) + 1;
    return Math.min(del, Math.min(ins, rep));
}

编辑:示例 字符串 1:“casa” 字符串 2:“卡拉” edit_distance=2(1 次删除 + 1 次插入)

编辑2: 这些是有效的字符串: 字符串 1:“casa”,字符串 2:“cassa”,edit_distance=1; 字符串 1:“pioppo”,字符串 2:“pioppo”,edit_distance=0;

这些是不起作用的: 字符串 1:“casa”,字符串 2:“cara”,edit_distance=2; (在我的代码中=0) 字符串 1:“tassa”,字符串 2:“passato”,edit_distance=4; (在我的代码中=2)

【问题讨论】:

  • 很高兴看到您正在尝试解决的问题的描述。
  • 请添加一些您使用过的测试字符串示例及其输出。我们需要能够将应该发生的事情与正在发生的事情进行比较。可能导致问题的一件事(我不是可以运行代码的人)是长度为 1 的任意长度的字符串。您从 0 开始递归。
  • 什么是代表?为什么以 1 为代价只在每个单词中跳过一个字母是可以的?似乎立即搞砸了您的示例并返回 1 而不是 2
  • 我用一些例子编辑了这个问题
  • 您的示例与您的代码不匹配。如果我只是运行你的代码,我会得到不同的数字。如果您不显示您的实际代码,我们无法告诉您您做错了什么。

标签: java sorting methods


【解决方案1】:

我认为实现几乎是正确的,您错过了停止条件。它们应该是:

if (j == s2.length()) {
    return s1.length() - i;
}
if (i == s1.length()) {
    return s2.length() - j;
}

所以完整的实现应该是:

private static int distance(String s1, String s2, int i, int j) {
    if (j == s2.length()) {
        return s1.length() - i;
    }
    if (i == s1.length()) {
        return s2.length() - j;
    }
    if (s1.charAt(i) == s2.charAt(j))
        return distance(s1, s2, i + 1, j + 1);
    int rep = distance(s1, s2, i + 1, j + 1) + 2; // since Jim Belushi considers replacement to be worth 2.
    int del = distance(s1, s2, i, j + 1) + 1;
    int ins = distance(s1, s2, i + 1, j) + 1;
    return Math.min(del, Math.min(ins, rep));
}

更新

这是“tassa”和“passato”的结果:

代码:

private static int distance(String s1, String s2, int i, int j) {
    if (j == s2.length()) {
        return s1.length() - i;
    }
    if (i == s1.length()) {
        return s2.length() - j;
    }
    if (s1.charAt(i) == s2.charAt(j))
        return distance(s1, s2, i + 1, j + 1);
    int rep = distance(s1, s2, i + 1, j + 1) + 2;
    int del = distance(s1, s2, i, j + 1) + 1;
    int ins = distance(s1, s2, i + 1, j) + 1;
    return Math.min(del, Math.min(ins, rep));
}

public static void main(String[] args) {
    int dist = distance("tassa", "passato", 0, 0);
    System.out.println(dist);
}

如果你运行它,你会得到:

4

【讨论】:

  • testFourDistance(editdistance.EditDistanceTests):预期:,但是:
  • 这是我的测试:assertEquals(4, EditDistance.distance("tassa", "passato"));
  • @JimBelushi: testFourDistance(editdistance.EditDistanceTests): 预期: 但是是:。测试中使用了哪些值?
  • “tassa”和“passato”
  • @JimBelushi:请查看我的更新回复。我也得到 4 个。
【解决方案2】:

这应该是你想要的

如果 char 的每次编辑都意味着距离 +2(= 删除 + 添加),它还会添加添加/删除的字符数 - 但只有 +1,而不是 +2

//get number of deletions / edits - inc 1 per each
public static void editDistance() {
    String s1 = "casa";
    String s2 = "cara";

    String longer;
    String shorter;
    if(s1.length() > s2.length()) {
        longer = s1;
        shorter = s2;
    }else {
        shorter = s1;
        longer = s2;
    }

    int edits = 0;
    for (int i = 0; i < shorter.length(); i++) {
        if(shorter.charAt(i) != longer.charAt(i)) {
            edits++;
        }
    }

    edits = edits *2; //one delete, one insert you told
    edits = edits + Math.abs(s1.length() - s2.length()); //if different length then add counts of added/removed chars 

    System.out.println("edit count: " + edits);

}

【讨论】:

    【解决方案3】:

    当你到达一个字符串的结尾而不是另一个字符串的结尾时,你需要指定如何继续,试试这个

    public static void main(String[] args) {
        System.out.println(distance("casa","cassa"));
    }
    
    public static int distance (String s1, String s2) {
        return distance(s1, s2, 0, 0);
    }
    
    private static int distance(String s1, String s2, int i, int j) {
        if (i == s1.length() && j==s2.length())
            return 0;
        else if(i== s1.length())
            return s2.length() - j;
        else if(j == s2.length())
            return s1.length() - i;
    
        if (s1.charAt(i) == s2.charAt(j))
            return distance(s1, s2, i + 1, j + 1);
    
        int rep = distance(s1, s2, i + 1, j + 1) + 1;
        int del = distance(s1, s2, i, j + 1) + 1;
        int ins = distance(s1, s2, i + 1, j) + 1;
        return Math.min(del, Math.min(ins, rep));
    }
    

    输出

    1
    

    注意:第一个if 不是必需的,只是让代码更易于理解......在你的impl中删除它

    【讨论】:

    • @JimBelushi 你确定吗?适合我,你能提供输入吗?
    • String s1 = "casa";字符串 s2 = "cassa"; assertEquals(1, EditDistance.distance(s1, s2));
    • @JimBelushi 有效,可能是您的测试语法有问题或其他问题......
    • @JimBelushi 好的,然后将 2 添加到该操作。请在下面查看我的更新答案。
    • @JimBelushi 有效,给出 4,再次检查你的语法,下面的代码与这篇文章的代码相同......但没有不必要的 if,给他积分!
    【解决方案4】:

    两个简单的更改和您的代码工作:

    第一:

        if (i == s1.length()) return s2.length() - j;
        if (j == s2.length()) return s1.length() - i;
    

    而不是

        if (i == s1.length()) return j;
        if (j == s2.length()) return i;
    

    下一步:

        int rep = distance(s1, s2, i + 1, j + 1) + 2;
    

    最后的 2 在这里很重要。如果 rep 表示替换,则它是一个删除和一个插入。使其成为两个操作,而不是 1。

    【讨论】:

      【解决方案5】:

      它适用于我:

      private static int distance(String s1, String s2, int i, int j) {
          if (i == s1.length() && j == s2.length()) {
              return 0;
          } else if (i == s1.length()) {
              return s2.length() - j;
          } else if (j == s2.length()) {
              return s1.length() - i;
          }
      
          if (s1.charAt(i) == s2.charAt(j)) {
              return distance(s1, s2, i + 1, j + 1);
          }
      
          // int rep = distance(s1, s2, i + 1, j + 1) + 1;
          int del = distance(s1, s2, i, j + 1) + 1;
          int ins = distance(s1, s2, i + 1, j) + 1;
          //  return Math.min(del, Math.min(ins, rep));
          return Math.min(del, ins);
      }
      

      有测试,它也有效:

      /**
       * Test of distanceRec method, of class EditDistance.
       */
      @Test
      public void testDistanceRec() {
          System.out.println("distanceRec");
          String s1 = "passato";
          String s2 = "tassa";
          int expResult = 4;
          int result = EditDistance.distanceRec(s1, s2);
          assertEquals(expResult, result);
          // Review the generated test code and remove the default call to fail.
          //fail("The test case is a prototype.");
      }
      

      在这个应用程序中,您只能使用两种操作:插入和删除,不能进行替换或匹配等其他操作。 练习文本:

      假设可用的操作只有两个:删除和插入一个字符。例子: - “casa”和“cassa”的编辑距离等于1(1次取消); - “casa”和“cara”的编辑距离等于 2(1 次取消 + 1 次插入); - “税”和“过去”的编辑距离等于 4(3 次取消 + 1 次插入); - "poplar" 和 "poplar" 的编辑距离为 0。

      【讨论】: