1. 程式人生 > >字串編輯距離 經典 dp

字串編輯距離 經典 dp

可以參考這個部落格:點選開啟連結

比較兩個字串相似度,可以通過最長公共子串,或者最長公共子序列,還有就是編輯距離。兩個字串通過插入,刪除或者修改,來達到一致,編輯距離越短,可知相似度越高。

#include <bits/stdc++.h>
using namespace std ;
#define Min( a , b , c ) ( a < b ? ( a < c ? a : c ) : ( b < c ? b : c ) )
int dp[1010][1010] ;
char s1[1010] , s2[1010] ;

int main(){
	int n , m , i , j , cost ;
	while( ~scanf("%s%s" , s1 , s2 ) ){
		n = (int)strlen( s1 ) ;
		m = (int)strlen( s2 ) ;
		for( i = 0 ; i <= n ; ++i ) dp[i][0] = i ;  // 另一個字串長度為 0 時,長度就是本字串當前長度
		for( i = 0 ; i <= m ; ++i ) dp[0][i] = i ;
		for( i = 1 ; i <= n ; ++i )
			for( j = 1 ; j <= m ; ++j ){
				cost = s1[i-1] == s2[j-1] ? 0 : 1 ;     // 當前字元相等,可以選擇修改 + cost
				dp[i][j] = Min( dp[i-1][j]+1 , dp[i][j-1]+1 , dp[i-1][j-1]+cost ) ;
			}    
		cout << dp[n][m] << endl ;
	}
	return 0 ;
}
因為當前字串肯定是根據長度 -1 的字串演變而來,從上面的 dp 也可以看出,i 只和 i-1 有關,所以為了節省空間,可以採用滾動陣列。時間複雜度不變,但是空間複雜度從 O(n*m) 降到O( 2*max( n , m )  ) 。
#include <bits/stdc++.h>
using namespace std ;
#define Min( a , b , c ) ( a < b ? ( a < c ? a : c ) : ( b < c ? b : c ) )
int dp[2][1010] ;
char s1[1010] , s2[1010] ;

int main(){
	int n , m , i , j , cost ;
	while( ~scanf("%s%s" , s1 , s2 ) ){
		n = (int)strlen( s1 ) ;
		m = (int)strlen( s2 ) ;
		memset( dp , 0 , sizeof( dp ) ) ;
		for( i = 0 ; i <= m ; ++i ) 
			dp[0][i] = i ;
		for( i = 1 ; i <= n ; ++i ){
			dp[i&1][0] = i ;
			for( j = 1 ; j <= m ; ++j ){
				cost = s1[i-1] == s2[j-1] ? 0 : 1 ;
				dp[i&1][j] = Min( dp[(i-1)&1][j]+1 , dp[i&1][j-1]+1 , dp[(i-1)&1][j-1]+cost ) ;
			}
		}
		cout << dp[n&1][m] << endl ;
	}
	return 0 ;
}
上面的部落格中還有一種空間複雜度 O( max( n , m ) ) 的解法, 利用了當前字元匹配相等,不需要做任何操作這一特點。
#include <bits/stdc++.h>
using namespace std ;
#define Min( a , b , c ) ( a < b ? ( a < c ? a : c ) : ( b < c ? b : c ) )
int dp[1010] ;
char s1[1010] , s2[1010] ;

int main(){
	int n , m , i , j , temp , cur ;
	while( ~scanf("%s%s" , s1 , s2 ) ){
		cout << "s1 = " << s1 << "\ts2 = " << s2 << endl ;
		n = (int)strlen( s1 ) ;
		m = (int)strlen( s2 ) ;
		for( i = 0 ; i <= m ; ++i ) 
			dp[i] = i ;
		for( i = 1 ; i <= n ; ++i ){
			cur = i-1 ;             // 因為另一個字串長度不為 0 , 初始編輯距離-1,對應二維的 dp[i-1]
			for( j = 1 ; j <= m ; ++j ){
				temp = dp[j] ;              
				dp[j] = s1[i-1] == s2[j-1] ? cur : 1 + Min( dp[j-1] , dp[j] , cur ) ;
				cur = temp ;          // 儲存下次的 dp[i-1][j-1] ;       
			}
		}
		cout << dp[m] << endl ;
	}
	return 0 ;
}