編輯距離(C++)


給定兩個字符串s1和s2,計算出將s1轉換成s2所用的最少操作數。可以對一個字符串進行如下三種操作:1.插入一個字符;2.刪除一個字符;3.替換一個字符。

遞歸法的解題方式:
兩個字符串從后(i=s1.length()-1,j=s2.length()-1)往前比較,遞歸三要素之一——終止條件base case:i=-1或j=-1結束;遞歸三要素之二——終止時的處理:返回另一個字符剩下的長度。即base case是i走完s1或j走完s2,可以直接返回另一個字符串剩下的長度,這是因為當i走完s1,j沒走完s2時,用插入操作把s2剩下的字符插入s1,需要j+1步(因為j從0開始);而當j走完s2而i沒走完s1時,用刪除操作把s1前面沒處理的字符刪掉,需要i+1步(因為j從0開始)。
遞歸要素之三————提取重復邏輯。當比較的兩個字符相同時,兩字符串都向前搜索;而兩個字符不同時,判斷插入、刪除、替換后哪個步驟最少,就從選那條路徑。不能理解的讀者可以看以前的一篇博文適合新手的遞歸理解。插入、刪除、替換是怎么操作的可以看下列代碼及注釋。

int dp(string s1, string s2, int i, int j)
{
	//i走完s1,j沒走完s2,用插入操作把s2剩下的字符插入s1,需要j+1步(因為j從0開始)
	if (i == -1)
		return j + 1;
	//j走完s2而i沒走完s1,用刪除操作把s1前面沒處理的字符刪掉,需要i+1步(因為j從0開始)
	if (j == -1)
		return i + 1;
	//兩個字符相同,直接往前移動i,j即可
	if (s1[i] == s2[j])
		return dp(s1, s2, i - 1, j - 1);
	else
		//插入:i不往前移,j往前移,說明只處理了s2,這是往s1中插入一個字符導致的。處理步驟+1。
		//刪除:i往前移,j不往前移,說明s1處理了一個字符而s2沒處理,這是s1中刪除了一個字符。處理步驟+1。
		//替換:i往前移,j往前移,說明既處理了s1,也處理了s2,是為替換字符。處理步驟+1。
		//遞歸步驟最少的那種(即把其他兩種步驟多的排除了)。
		return min(min(dp(s1, s2, i, j - 1) + 1, dp(s1, s2, i - 1, j) + 1), dp(s1, s2, i - 1, j - 1) + 1);
}

動態規划代碼:
單純的遞歸代碼會有很多重復計算的步驟,畫出樹形結構就能直觀看出,因此采用動態規划法,利用備忘錄數組dp[][]來保存結果,
動態規划代碼首先遇到的問題是數組的初始值,當s2為空,那么s1的長度就是要刪除的步驟數,即dp[i][0]=i;當s1為空,s2的長度就是s1要插入的步驟數,即dp[0][j]=j。然后自底向上求解,遞歸是自頂向下。

int minDistance2(string s1, string s2)
{
	int m = s1.length();
	int n = s2.length();
	vector<vector<int>> dp(m + 1, vector<int>(n + 1));
	for (int i = 1; i <= m; i++)//<=說明遍歷的次數是m-1+1次,即字符串的長度
	{
		dp[i][0] = i;
	}
	for (int j = 1; j <= n; j++)
	{
		dp[0][j] = j;
	}
	for (int i = 1; i <= m; i++)
	{
		for (int j = 1; j <= n; j++)
		{
			//if (s1[i] == s2[j])//運行結果不對,因為第i個字符的索引為i-1
			if (s1[i-1] == s2[j-1])
			{
				dp[i][j] = dp[i - 1][j - 1];//第i行j列的步驟數等於第i-1行j-1列,因為字符相同不需什么操作,所以不用+1
			}
			else
			{
				dp[i][j] = min(min(dp[i - 1][j]+1, dp[i][j - 1]+1), dp[i - 1][j - 1]+1);//+1表示經過了一次操作
			}
		}
	}
	return dp[m][n];
}


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM