C語言 · 最長公共子序列 · 最長字符序列


算法提高篇有兩個此類題目:
算法提高 最長字符序列  
時間限制:1.0s   內存限制:256.0MB
    
  最長字符序列
問題描述
  設x(i), y(i), z(i)表示單個字符,則X={x(1)x(2)……x(m)},Y={y(1)y(2)……y(n)},Z={z(1)z(2)……z(k)},我們稱其為字符序列,其中m,n和k分別是字符序列X,Y,Z的長度,括號()中的數字被稱作字符序列的下標。
  如果存在一個嚴格遞增而且長度大於0的下標序列{i1,i2……ik},使得對所有的j=1,2,……k,有x(ij)=z(j),那么我們稱Z是X的字符子序列。而且,如果Z既是X的字符子序列又是Y的字符子序列,那么我們稱Z為X和Y的公共字符序列。
  在我們今天的問題中,我們希望計算兩個給定字符序列X和Y的最大長度的公共字符序列,這里我們只要求輸出這個最大長度公共子序列對應的長度值。
  舉例來說,字符序列X=abcd,Y=acde,那么它們的最大長度為3,相應的公共字符序列為acd。
輸入格式
  輸入一行,用空格隔開的兩個字符串
輸出格式
  輸出這兩個字符序列對應的最大長度公共字符序列的長度值
樣例輸入
aAbB aabb
樣例輸出
2
數據規模和約定
  輸入字符串長度最長為100,區分大小寫。
 
算法提高 最長公共子序列  
時間限制:1.0s   內存限制:256.0MB
    
問題描述
  給定兩個字符串,尋找這兩個字串之間的最長公共子序列。
輸入格式
  輸入兩行,分別包含一個字符串,僅含有小寫字母。
輸出格式
  最長公共子序列的長度。
樣例輸入
abcdgh
aedfhb
樣例輸出
3
樣例說明
  最長公共子序列為a,d,h。
數據規模和約定
  字串長度1~1000。
 
 
作者注釋:遞歸超時,遞推公式如下:
 1 /*
 2 遞歸思路:
 3     當數組a和b對應位置字符相同時,則直接求解下一個位置;
 4                           否則,取兩種情況中的較大數值。 
 5 */
 6 #include<stdio.h>
 7 #include<string.h>
 8 char a[101],b[101];//定義字符串數組
 9 int lena,lenb,lenz=0;
10 int maxlong(int i,int j){
11     if(i>=lena || j>=lenb) return 0;//出口 
12     if(a[i] == b[j]) return 1+maxlong(i+1,j+1);//直接求解下一個位置
13     else //取兩種情況中的較大數值
14         return maxlong(i+1,j) > maxlong(i,j+1) ? maxlong(i+1,j) : maxlong(i,j+1);
15 }
16 int main(){
17     scanf("%s%s",&a,&b);//輸入字符串
18     lena=strlen(a);//獲取字符串長度 
19     lenb=strlen(b);
20     printf("%d",maxlong(0,0));//輸出子串長度
21     return 0; 
22 }              

 

動態規划:時間復雜度大大降低。

 1 #include<stdio.h>  
 2 #include<string.h>  
 3 char a[101],b[101];  
 4 char num[102][102];//記錄中間結果的數組  
 5 //動態規划采用二維數組來標識中間計算結果,避免重復的計算來提高效率
 6 void LCS(int lena,int lenb){ 
 7     for(int i=1;i<=lena;i++){
 8         for(int j=1;j<=lenb;j++){
 9             if(a[i-1]==b[j-1]){//注意這里的下標是i-1與j-1  
10                 num[i][j]=num[i-1][j-1]+1;
11             }  
12             else{
13                 num[i][j]=num[i][j-1] > num[i-1][j] ? num[i][j-1] : num[i-1][j];
14             }  
15         }  
16     }  
17 }
18 int main(){  
19     scanf("%s%s",&a,&b);//輸入字符串
20     int lena = strlen(a);//獲取字符串長度 
21     int lenb = strlen(b);
22     memset(num,0,sizeof(num));//數組賦初值  
23     LCS(lena,lenb);
24     printf("%d",num[lena][lenb]); 
25     return 0;
26 }  

 

求兩字符串的最長子串,有兩種情況:一種是不要求在原串中連續(即上述情況),另一種要求子串在原字符串中是連續的。

下面轉載了網上的方法來做另一種情況的。

用矩陣來記錄中間結果:例如原串"bab"和"caba",則數組如下:

   b    a     b

c      0    0    0

a      0    1 0

b      1 0    1

a      0    1 0

 

則矩陣的斜對角線最長的那個就是我們找的最長公共子串——求最長的由1組成的斜對角線:當要在矩陣是填1時讓它等於其左上角元素加1,如下:

      b    a     b

c      0    0    0

a      0    1 0

b      1 0    2

a      0    2 0

這樣矩陣中的最大元素就是 最長公共子串的長度。

在構造這個二維矩陣的過程中由於得出矩陣的某一行后其上一行就沒用了,所以實際上在程序中可以用一維數組來代替這個矩陣(降低空間復雜度)。

以下代碼來自網絡:

 1 #include<iostream>  
 2 #include<cstring>  
 3 #include<vector>  
 4 using namespace std;  
 5 //str1為橫向,str2這縱向  
 6 const string LCS(const string& str1,const string& str2){  
 7     int xlen=str1.size();       //橫向長度  
 8     vector<int> tmp(xlen);        //保存矩陣的上一行  
 9     vector<int> arr(tmp);     //當前行  
10     int ylen=str2.size();       //縱向長度  
11     int maxele=0;               //矩陣元素中的最大值  
12     int pos=0;                  //矩陣元素最大值出現在第幾列  
13     for(int i=0;i<ylen;i++){  
14         string s=str2.substr(i,1);  
15         arr.assign(xlen,0);     //數組清0  
16         for(int j=0;j<xlen;j++){  
17             if(str1.compare(j,1,s)==0){  
18                 if(j==0)  
19                     arr[j]=1;  
20                 else  
21                     arr[j]=tmp[j-1]+1;  
22                 if(arr[j]>maxele){  
23                     maxele=arr[j];  
24                     pos=j;  
25                 }  
26             }         
27         }  
28         tmp.assign(arr.begin(),arr.end());  
29     }  
30     string res=str1.substr(pos-maxele+1,maxele);  
31     return res;  
32 }  
33 int main(){  
34     string str1("21232523311324");  
35     string str2("312123223445");  
36     string lcs=LCS(str1,str2);  
37     cout<<lcs<<endl;  
38     return 0;  
39 }

 

最長公共子序列應用

有這樣的題目:

  回文串是指正反都相同的字符串,比如abba,他正着看反着看都相同,給你一個字符串,長度小於1000,讓你刪除最少的字符使得該字符串成為回文串,例子:agddgca,刪除字符c就可以組成一個回文串。問最少刪除幾個字符?

  想到可以借助最長公共子序列來解決,我們那上面那個例子看一下,我們把這個字符轉倒序:acgddga,可以看到他們的最長公共子序列剛好是最長回文串。其實倒過來的字符串和原來的字符串比較,就是那前面的跟后面的字符串比較,如果一樣,那么他們就是回文了。

所以求解這道題就比較簡單了:

1)先將字符串倒序排列

2)求兩個字符串飛串的最長公共子序列

3)用字符串的長度減去最長公共子序列就得到結果


免責聲明!

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



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