1. 程式人生 > >字符串編輯距離

字符串編輯距離

article 字符串編輯距離 選擇 ret 通過 刪除字符 int 長度 問題

字符串編輯距離

字符串的編輯距離,又稱為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

字符串編輯距離