字符串编辑距离

Posted boris1221

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了字符串编辑距离相关的知识,希望对你有一定的参考价值。

字符串编辑距离

字符串的编辑距离,又称为Levenshtein距离,由俄罗斯的数学家Vladimir Levenshtein在1965年提出。是指利用字符操作,把字符串A转换成字符串B所需要的最少操作数。其中,字符操作包括:

  • 删除一个字符     
  • 插入一个字符     
  • 修改一个字符     

例如对于字符串"if"和"iff",可以通过插入一个‘f‘或者删除一个‘f‘来达到目的。

问题描述:给定两个字符串A和B,求字符串A至少经过多少步字符操作变成字符串B。

我们先以一个例子分析,比如eat变成tea。对于第一个字符,e != a,所以要想让这两个字符相等,有三种可以选择的办法

  • 修改字符,将e直接变成a,需要走1步。
  • 插入字符,在e的前面插入a,也需要走1步。
  • 删除字符,将e删除,然后比较后面的与a,也需要走1步。

如果是 e==a,那么就可以直接跳过这个字符比较下面的字符,那么他们的距离也就是前面一步的举例了。

经过举例子分析,很容易发现这是一个动态规划问题,那么我们就按照动态规划的一套方法来求解。

1、维护一个dp数组,其中dp[i][j]表示s1[0]---s1[i]和s2[0]--s2[j]相同需要进行的最少步骤;

2、边界条件初始化,dp[i][0]=i,相当于将s1挨个变成空所要进行的步数,对于dp[0][j]=j同理;

3、状态转移方程,我们要得到dp[i][j]的值,假设s1[i-1]和s2[j-1]之前的都已经相等了,那么如果s1[i]==s2[j],显然不需要进行操作,dp[i][j]==dp[i-1][j-1];如果s1[i]!=s2[j],那么到达dp[i][j]的就有三条路,分别从dp[i-1][j-1]、dp[i-1][j]、dp[i][j-1],对应的含义分别是修改字符、删除字符和插入字符,在三种操作下,经历的步数都要+1,所以我们只要找三者的最小值然后+1就可以了。

这个题目有一种巧妙的理解办法,就是画表格。画表格法在动态规划太有用了!!!特别是处理这种数组是二维的情况,可以直观的理解状态转移的过程,非常值得学习。

这里以s1="cafe"  s2="coffee"。表格如下:

(1)初始状态,这里要注意dp数组的长度要比字符串长度+1,因为要保存字符串为空的状态
      c o f f e e
                       
c                     
a                     
f                     
e                   
(2)边界条件初始化,
      c o f f e e
   0 1 2 3 4 5 6
c 1                  
a 2                  
f 3                  
e 4                
(3)状态转移
我们以3,3为例,开始计算。因为c==c,所以3,3格和2,2格相同,都为0。
对于3,4,因为c!=o,所以到达3,4格有三个方向,我们取以下三个值的最小值:
  • 对角数字+1(对于3,4来说为2)
  • 左方数字+1(对于3,4格来说为1)
  • 上方数字+1(对于3,4格来说为3)
因此为格3,4为1
      c o f f e e
   0 1 2 3 4 5 6
c 1 0  1             
a 2                  
f 3                  
e 4                   
循环操作,推出下表
      c o f f e e
   0 1 2 3 4 5 6
c 1 0 1 2 3    4    5   
a 2 1 1 2 3 4 5
f 3 2 2 1 2 3 4
e 4 3 3 2 2 2 3
取右下角,得编辑距离为3
求解字符串编辑距离方法大概就是如此,想明白之后还是挺简单的。主要还是会通过表格来找状态转移过程。
代码如下:
 1 public void mineditdistance(){
 2         String s1 = "cafe";
 3         String s2 = "coffee";
 4         int[][] dp = new int[s1.length()+1][s2.length()+1];
 5         //对dp数组初始化
 6         for ( int i = 0 ; i < dp.length ; i ++ ) dp[i][0] = i;
 7         for ( int j = 0 ; j < dp[0].length ; j ++ ) dp[0][j] = j;
 8 
 9         for ( int i = 1 ; i < dp.length ; i ++ ){
10             for ( int j = 1 ; j < dp[0].length; j ++ ){
11                 if ( s1.charAt(i-1) == s2.charAt(j-1) ) dp[i][j] = dp[i-1][j-1];
12                 else dp[i][j] = min(dp[i-1][j],dp[i][j-1],dp[i-1][j-1])+1;
13             }
14         }
15         System.out.println(dp[dp.length-1][dp[0].length-1]);
16     }
17     public int min(int a, int b, int c){
18         return Math.min(a,Math.min(b,c));
19     }

      这个是面试时问的问题,在状态转移方程地方卡住了。回来好好又分析了一遍,颇有收获。

      字符串+极值  问题,第一个想到的就是用dp把。还有很多类似的问题,后续会慢慢总结。

      例题参考:https://blog.csdn.net/ac540101928/article/details/52786435

以上是关于字符串编辑距离的主要内容,如果未能解决你的问题,请参考以下文章

代码随想录算法训练营第五十六天 | 583. 两个字符串的删除操作72. 编辑距离编辑距离总结

java刷题--编辑距离

基于编辑距离来判断词语相似度方法(scala版)

Levenshtein distance 编辑距离

计算字符串的距离 --- 动态规划

动态规划经典题之编辑距离