演算法:編輯距離問題(動態規劃,詳細解答)

坤輿湖畔的萬某某發表於2020-11-15

一、問題描述:

設A和B是2個字串,要用最少的字元操作將字串A轉化為字串B。這裡所說的字元操作包括:(1)刪除一個字元;(2)插入一個字元;(3)將一個字元改為另一個字元。將字串A變換為B所用的最少字元運算元稱為字串A到B的編輯距離,記為d(A,B)。試設計一個有效演算法,對任給的2個字串A和B,計算出它們的編輯距離d(A,B)。

示例:

輸入:

fxpimu

xwrs

輸出:

5

 

二、思路分析:

解法一:直接利用求解最長公共子序列的演算法求出最長公共子序列的長度,然後使用較長字串的長度減去最長公共子序列的長度就是所需的最短步驟。

求解最長公共子序列的演算法的詳解解答見之前的部落格文章,這是連結:https://blog.csdn.net/weixin_46013401/article/details/109329667

解法二:利用二維陣列distance記錄求解的過程,利用其下標表示指向不同字串的元素。

當兩個字元元素相等時,distance[i][j] = distance[i-1][j-1];

當兩個字元元素不相等時,分為三種情況選取其中所需步驟最少的一個:

第一種:修改其中一個字串的元素,使得雙方待比較的元素都減一。

distance[i][j] = distance[i-1][j-1]+1;

第二種:刪除第一個字串的元素或者新增第二個字串的元素,使之相等

distance[i][j] = distance[i-1][j]+1;

第三種:刪除第二個字串的元素或者新增第一個字串的元素,使之相等

distance[i][j] = distance[i][j-1]+1;
for(int i = 1;i<=m;i++)
			for(int j = 1;j<=n;j++) {
				if(x[i]==y[j]) //兩個字串對應的元素相等
					distance[i][j] = distance[i-1][j-1];
				else//不相等時分為三種操作型別,選擇其中所需步驟最小的情況!(和後一行程式碼操作順序是一一對應的)
					//第一種:修改其中一個字串的元素,使得雙方待比較的元素都減一
					//第二種:刪除第一個字串的元素或者新增第二個字串的元素,使之相等
					//第三種:刪除第二個字串的元素或者新增第一個字串的元素,使之相等
					distance[i][j] = minOfThree(distance[i-1][j-1], distance[i-1][j], distance[i][j-1])+1;
			}

最終在distance[m][n]處獲得最終結果

 

三、程式碼求解:

解法一:

import java.util.Scanner;

public class demo{
	public static void main(String[] args) {
		Scanner s = new Scanner(System.in);
		System.out.println("請輸入第一個字串:");
		String str1 = s.nextLine();
		System.out.println("請輸入第二個字串:");
		String str2 = s.nextLine();
		//str1 to str2
		
		int m = str1.length();//第一個字串的長度
		int n = str2.length();//第二個字串的長度
		
		int[][] c = new int[m+1][n+1];
		//用於記錄其求解過程
		int[][] b = new int[m+1][n+1];
		//用於記錄其求解路徑
		
		char[] x,y;//統一下標,便於求解
		x = new char[m+1];
		y = new char[n+1];
		for(int i = 1;i<=m;i++)
			x[i] = str1.charAt(i-1);
		for(int j = 1;j<=n;j++)
			y[j] = str2.charAt(j-1);
		
		//呼叫函式進行求解
		maxLength(m, n, x, y, c, b);
		maxAnswer(m, n, x, b);
		int max = m>n?m:n;
		System.out.println("需要的步數為:"+(max-count)+"steps");
		
		
	}
	
	public static void maxLength(int m,int n,char[]x,char[]y,int[][]c,int[][]b) {
		//對於陣列c進行初始化
		for(int i = 0;i<=m;i++)
			c[i][0] = 0;
		for(int j = 0;j<=n;j++)
			c[0][j] = 0;
		
		for(int i = 1;i<=m;i++)
			for(int j = 1;j<=n;j++) {
				if(charEqual(x[i], y[j])) {
					c[i][j] = c[i-1][j-1]+1;//取對角,兩個字串都減一
					b[i][j] = 1;
				}else if(c[i-1][j]>=c[i][j-1]) {//取更大的值,說明解更優
					c[i][j] = c[i-1][j];//取上面的
					b[i][j] = 2;
				}else {//取左邊的
					c[i][j] = c[i][j-1];
					b[i][j] = 3;
				}
			}
	}
	
	//用於比較兩個字元常量是否相等
	public static boolean charEqual(char a,char b) {
		Character A = new Character(a);
		Character B = new Character(b);
		if(A.equals(B))
			return true;
		return false;
	}
	
	public static int count = 0;//計數
	//用於輸出結果
	public static void maxAnswer(int i,int j,char[] x,int[][]b) {	
		if(i==0||j==0)
			return;
		
		//相當於對求解過程思路的原路回溯求解
		if(b[i][j]==1) {
			maxAnswer(i-1, j-1, x, b);//右上角
			count++;
		}else if(b[i][j]==2) {
			maxAnswer(i-1, j, x, b);//上面
		}else {
			maxAnswer(i, j-1, x, b);//左邊
		}
		
	}
}

解法二:

import java.util.Scanner;

public class demo{
	public static void main(String[] args) {
		String str1,str2;//用於接受輸入的字串
		Scanner s = new Scanner(System.in);
		System.out.println("請輸入第一個字串:");
		str1 = s.nextLine();
		System.out.println("請輸入第二個字串:");
		str2 = s.nextLine();
		
		int m = str1.length();//length
		int n = str2.length();
		
		char[] x = new char[m+1];//統一下標,便於後續操作
		for(int i = 1;i<=m;i++)
			x[i] = str1.charAt(i-1);
		
		char[]y = new char[n+1];
		for(int j = 1;j<=n;j++)
			y[j] = str2.charAt(j-1);
		
		System.out.println("最少需要"+minSteps(m, n, x, y)+"steps");
	}
	
	public static int minSteps(int m,int n,char[] x,char[] y) {
		int [][]distance = new int[m+1][n+1];
		
		//初始化,當轉換的字串一方為空時,轉換步數為另一方的字串長度
		for(int i = 1;i<=m;i++)
			distance[i][0] = i;
		for(int j = 1;j<=n;j++)
			distance[0][j] = j;
		
		for(int i = 1;i<=m;i++)
			for(int j = 1;j<=n;j++) {
				if(x[i]==y[j]) //兩個字串對應的元素相等
					distance[i][j] = distance[i-1][j-1];
				else//不相等時分為三種操作型別,選擇其中所需步驟最小的情況!(和後一行程式碼操作順序是一一對應的)
					//第一種:修改其中一個字串的元素,使得雙方待比較的元素都減一
					//第二種:刪除第一個字串的元素或者新增第二個字串的元素,使之相等
					//第三種:刪除第二個字串的元素或者新增第一個字串的元素,使之相等
					distance[i][j] = minOfThree(distance[i-1][j-1], distance[i-1][j], distance[i][j-1])+1;
			}
		return distance[m][n];
	}
	
	public static int minOfThree(int x,int y,int z) {
		int flag = x<y?x:y;
		return (flag<z?flag:z);
	}
}

 

四、執行結果:

 

五、分析:

 時間複雜度空間複雜度
解法一

O(m*n)

O(m*n)

解法二

O(m*n)

O(m*n)

相關文章