源串修改為目標串共操作的次數以及最長公共子串
阿新 • • 發佈:2019-02-07
給定一個源串和目標串,能夠對源串進行如下操作:
1).在給定位置上插入一個字元
2).替換任意字元
3).刪除任意字元
寫一個程式,返回最小操作次數,使得對源串進行這些操作後等於目標串。
不過還有一個辦法是利用程式設計之美上的最長形同字串,然後用strA與strB的長度和減去共同的字串長度即可得到最小的操作次數
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); }