1. 程式人生 > >源串修改為目標串共操作的次數以及最長公共子串

源串修改為目標串共操作的次數以及最長公共子串

給定一個源串和目標串,能夠對源串進行如下操作:
1).在給定位置上插入一個字元
2).替換任意字元
3).刪除任意字元
寫一個程式,返回最小操作次數,使得對源串進行這些操作後等於目標串。

例如:源串”hello”,目標串”lleo”,則通過3次修改可以使得源串變成目標串(刪除’h',刪除’e',在’o'之前插入字元’e')

分析:這和程式設計之美中求兩串的最長子序列很類似,我們同樣採用動態規劃的方法求解。首先需要確定的是該題的最優子結構,然後用普通的迴圈,或遞迴,或備忘錄的方式來實現。設f[i][j]表示源串strA[1..i]變成目標串strB[1..j]所需改動的最小次數,當i=0時表示源串沒有字元那麼f[0][j]=j;當j=0時,表示目標串沒有字元,所以f[i][0]=i;(在動態規劃演算法中這個初始化,或者叫邊界條件很重要!);如果strA[i]==strB[j]那麼f[i][j]=f[i-1][j-1];如果不等那麼可以有三種辦法

1.插入一個相同的字元,對應的f[i][j]=f[i][j-1]+1;

2.刪除那個不同的字元,對應的f[i][j]=f[i-1][j]+1;

3.替換一個相同的字元,對應的f[i][j]=f[i-1][j-1]+1;

這樣原理弄清楚了,開始寫程式碼了:

程式碼如下:

//  [10/7/2013 qingezha].給定一個源串和目標串,能夠對源串進行如下操作:
//在給定位置上插入一個字元
//	2).替換任意字元
//	3).刪除任意字元
//	寫一個程式,返回最小操作次數,使得對源串進行這些操作後等於目標串。
//	例如:源串”hello”,目標串”lleo”,則通過3次修改可以使得源串變成目標串(刪除’h',刪除’e',在’o'之前插入字元’e')
//也可以通過找到最長公共字串,然後2個原來串的長度和減去公共字串的長度即可
int cal_distance(const char *sta,const char *stb)
{
	if(sta == NULL || stb == NULL)
		return 0;  
	int f[10+1][5+1]={0};		//這裡可以用new一個一維陣列,代替棧上的二維陣列,因為棧上的編譯時就確定長度,堆上的執行時才確定
								//這裡純用於測試
	for (int i=0;i<11;++i) 
		f[i][0]=i;				//悲劇啊,這裡誤寫成0 了
	for (int i=0;i<6;++i) 
		f[0][i]=i;				//悲劇啊,這裡誤寫成0 了
	int temp = 0;
	for (int j=1;j<6;++j)	
	{
		for (int i=1;i<11;++i)	//j<6寫成了i<6,以後要小心啊
		{
			if(sta[i]==stb[j])
				f[i][j]=f[i-1][j-1];
			else
			{
				temp = min(f[i-1][j-1]+1,f[i-1][j]+1);
				f[i][j]=min(f[i][j-1]+1,temp);
			}
			cout<<i<<" "<<j<<" "<<f[i][j]<<endl;
		}
	}
	return f[10][5];
	 
}

不過還有一個辦法是利用程式設計之美上的最長形同字串,然後用strA與strB的長度和減去共同的字串長度即可得到最小的操作次數

以下是求最長公共字串程式碼:

//  [7/7/2013 qingezha] 動態規劃 最長公共子序列 c[i][j]表示Xi={x1,x2,x3。。。xi}與Yj={y1,y2,。。。yj}的最長公共字串序列的長度
int lcs_length(char x[],char y[],int b[LA+1][LB+1],int c[LA+1][LB+1])
{
 
	for (int i=1;i<=LA;++i)  c[i][0]=0; //陣列需要初始化,否則值不一定
	for (int i=1;i<=LB;++i)  c[0][i]=0;
	for (int i=1;i<=LA;++i)
	{
		for (int j=1;j<=LB;++j)
		{
			if (x[i]==y[j])      //序列從1開始計數
			{
				c[i][j]=c[i-1][j-1]+1;//把c[i][j] 寫成了c[i][i]了,重大失誤,造成4個小時的浪費
				b[i][j]=1;				//b記錄c的值由哪一個子問題的解得到的
			} 
			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;
			}
		}
	}
	////////測試程式碼//////////////////////////////////////////////////////////////////
	for (int i=1;i<=LA;++i)           //可以將資料輸出來看看,哪裡有不符合邏輯的錯誤
	{
		for (int j=1;j<=LB;++j)
		{
			cout<<b[i][j];
		}
		cout<<endl;
	}
	cout<<endl;
	for (int i=1;i<=LA;++i)
	{
		for (int j=1;j<=LB;++j)
		{
			cout<<c[i][j];
		}
		cout<<endl;
	}
	cout<<endl;
	 
	char x[7+2]=" abcdefx";
	char y[7+2]=" aecxdfx";
	int b[8][8]={{0}};
	int c[8][8]={{0}};
	int a[8]={0};
	cout<<lcs_length(x,y,b,c)<<endl;
	lcs(7,7,x,b);
	//////////////////////////////////////////////////////////////////////////
	return c[LA][LB];
}

void lcs(int i,int j,char x[],int b[LA+1][LB+1])//輸出最長的公共字串
{

	if(0==i||0==j) return;
	if (b[i][j]==1)
	{
		lcs(i-1,j-1,x,b);
		cout<<x[i]<<" ";
	}
	else if(2==b[i][j])
		lcs(i-1,j,x,b);
	else lcs(i,j-1,x,b);
}