- > 動規講解基礎講解六——編輯距離問題

給定兩個字符串S和T,對於T咱們容許三種操做:ios


(1) 在任意位置添加任意字符
(2) 刪除存在的任意字符
(3) 修改任意字符 

問最少操做多少次能夠把字符串T變成S? 

例如: S=  「ABCF」   T = 「DBFG」

那麼咱們能夠

(1) 把D改成A
(2) 刪掉G
(3) 加入C

因此答案是3。
 
分析: 這個最少的操做次數,一般被稱之爲編輯距離。「編輯距離」一次自己具備最短的意思在裏面。由於題目有「最短」這樣的關鍵詞,首先咱們想到的是BFS。是的,當S的距離爲m, T的距離爲n的時候,咱們能夠找到這樣的操做次數的界限:

(1) 把T中字符全刪了,再添加S的所有字符,操做次數m + n。
(2) 把T中字符刪或加成m個,再修改 操做次數最多 |n – m| + m。

雖然,咱們找到了這樣的上界,BFS從實際角度並不可行,由於搜索空間是指數的,這取決於S中的字符種類——具體的數量級很差估計。
這個問題之因此難,是難在有「添加」「刪除」這樣的操做,很麻煩。咱們試試換個角度理解問題,把它當作字符串對齊的問題,事實上從生物信息學對比基因的角度,咱們能夠這樣理解問題。

給定字符串S和T,咱們能夠用一種特殊字符促成兩個字符串的對齊。咱們加的特殊字符是「-」, 咱們容許在S和T中任意添加這種特殊字符使得它長度相同,而後讓這兩個串「對齊」,最終兩個串相同位置出現了不一樣字符,就扣1分,咱們要使得這兩個串對齊扣分儘可能少。

對於例子 咱們實際上採起了這樣的對齊方式:

12345
ABCF-
DB-FG

注意:若是要對齊,兩個「-」相對是沒有意義的,因此咱們要求不出現這種狀況。
那麼看一下:
(1) S,T對應位置都是普通字符,相同,則不扣分。 例如位置2,4
(2) S,T對應位置都是普通字符,不一樣,則扣1分。 例如位置1
(3) S在該位置是特殊字符,T在該位置是普通字符,則扣1分,例如位置5
(4) S在該位置是普通字符,T在該位置是特殊字符,則扣1分,例如位置3

咱們來看看扣分項目對應什麼?

(1) 不扣分,直接對應
(2) 對應把T中對應位置的字符修改
(3) 對應在T中刪除該字符
(4) 對應在T中添加該字符

好了,目標明確,感受像不像 LCS?咱們嘗試一下:
設f(i,j)表示S的前i位和T的前j位對齊後的最少扣分。

那咱們來看看最後一位,對齊的狀況

(1) 必須S[i] == T[j], 這時前i – 1和j – 1位都已經對齊了,這部分確定要最少扣分。這種狀況下最少的扣分是f(i-1,j-1)
(2) 和(1)相似,S[i]≠T[j],這種狀況下最少的扣分是f(i -1, j – 1) + 1
(3) S的前i位和T的前(j – 1)位已經對齊了,這部分扣分也要最少。這種狀況下最少的扣分是f(i,j-1) + 1
(4) S的前(i-1)位已經和T的前j位對齊了,這部分扣分要最少。這種狀況下最少的扣分是f(i,j-1) + 1

具體f(i,j)取什麼值,顯然是要看哪一種狀況的扣分最少。
爲了方便,咱們定義函數same(i,j)表示若是S[i] == T[j]則爲0,不然爲1。

咱們來表示一下遞推式:

f(i,j) = min(f(i – 1, j – 1) + same(i,j), f(i – 1,j ) + 1, f(i, j – 1) + 1)

初值是什麼?

f(0, j) = j
f(i, 0) = i

這時由於對於S的前0位,咱們只能在以前加入「-」,或者說把T所有刪掉了。相似地,對於T地前0位,咱們只能把S的字符都加進來,別無選擇。
注意上述兩個式子的重合點 f(0,0) = 0也符合咱們的定義,並不矛盾。

時間複雜度? O(m * n),空間複雜度? O(m * n)。一樣咱們發現到f(i,j)只與本行和上一行有關,能夠省掉一維的空間複雜度,從而達到O(n)。
優化後的僞代碼:
for j = 0 to n do
    f[j] = j
endfor

for i = 1 to m do
    last = f[0]
    f[0] = i
    for j = 1 to n do 
        temp = f[i,j]
        f[i,j] = min(last + same(i,j), temp + 1, f[j – 1] + 1)
        last = temp
    endfor
endfor

注意: 咱們對於i實際上更新j的順序是由小到達的,因此咱們須要保存「舊的」f[i-1,j – 1]。函數

題解:優化

#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
char a[1010],b[1010];
int f[1010][1010];
int main()
{
    cin>>a;
    cin>>b;
    int m=strlen(a);
    int n=strlen(b);
    for(int i=1;i<=m;i++)    f[i][0]=i;
    for(int j=1;j<=n;j++)    f[0][j]=j;
    for(int i=1;i<=m;i++)
      for(int j=1;j<=n;j++)
          if(a[i-1]==b[j-1])    f[i][j]=f[i-1][j-1];
          else f[i][j]=min(min(f[i-1][j-1],f[i-1][j]),f[i][j-1])+1;
    cout<<f[m][n];    
} 

   若是對你有所幫助,別忘了加好評哦;麼麼噠!!下次見!88spa

相關文章
相關標籤/搜索