最长公共子序列、最长公共子串、最小编辑距离三种算法总结

  最长公共子序列、最长公共子串、最小编辑距离是三种常见的字符串比较算法,考虑到其中的动态规划思想、状态转移方程比较类似,实现的方法也是如出一辙,这里将其状态转移方程和相应的实现代码做一个总结。

1、最长公共子序列(longest common sequence)

  状态转移方程:

  实现代码:

 1 int LCSeq(string str1, string str2)
 2 {
 3     if(str1.size()==0 || str2.size()==0)return 0;
 4     //分配内存dp[str1.size()+1][str2.size()+1]; 
 5     int **dp=new int*[str1.size()+1];
 6     for(int i=0; i<str1.size()+1; ++i)
 7     {
 8         dp[i]=new int[str2.size()+1];
 9     }
10     //初始化数组
11     //dp[i][0]=0 and dp[0][j]=0;
12     for(int i=0; i<str1.size()+1; ++i)dp[i][0]=0;
13     for(int j=0; j<str2.size()+1; ++j)dp[0][j]=0;
14     //填数组
15     for(int i=1; i<str1.size()+1; ++i)
16     {
17         for(int j=1; j<str2.size()+1; ++j)
18         {
19             if(str1[i-1]==str2[j-1])dp[i][j]=dp[i-1][j-1]+1;//注意此处应减1,因为dp二维数组的长和宽都加了1 
20             else dp[i][j]=max(dp[i-1][j], dp[i][j-1]);
21         }    
22     }
23     int result=dp[str1.size()][str2.size()];
24     //释放空间
25     for(int i=0; i<str1.size()+1; ++i)
26     {
27         delete [] dp[i];    
28     }
29     delete [] dp;
30     return result;
31 }

   当然了,如果想把最长的公共子序列打印出来,也是可以的,只需要求出dp数组后,由右下角往左上角推就可以了。考虑到两个字符串的最长公共子序列可能有多个,所以有以下两种写法,分别打印一个和多个最长公共子序列:

 1 void printOne(string str1, string str2, int **dp)
 2 {
 3     int i=str1.size();
 4     int j=str2.size();
 5     string tmp;
 6     while(i>0 && j>0)
 7     {
 8         if(str1[i-1]==str2[j-1]){
 9             tmp=str1[i-1]+tmp;
10             --i;
11             --j;
12         }else{
13             if(dp[i-1][j]>=dp[i][j-1]){
14                 --i;
15             }else{
16                 --j;
17             }
18         }
19     }
20     cout<<tmp<<endl;
21 }
22 bool isExist(vector<string> &result, string tmp)
23 {
24     for(int i=0; i<result.size(); ++i)
25     {
26         if(result[i]==tmp)return true;
27     }
28     return false;
29 }
30 void printAllCore(string &str1, string &str2, int **dp, int i, int j, vector<string> &result, string tmp)
31 {
32     if(i==0 || j==0){
33         if(!isExist(result, tmp))result.push_back(tmp);//如果之前不存在就加入,避免重复 
34         return;
35     }
36     if(str1[i-1]==str2[j-1]){
37         printAllCore(str1, str2, dp, i-1, j-1, result, str1[i-1]+tmp);
38     }else{
39         if(dp[i-1][j]>dp[i][j-1])printAllCore(str1, str2, dp, i-1, j, result, tmp);
40         else if(dp[i-1][j]<dp[i][j-1])printAllCore(str1, str2, dp, i, j-1, result, tmp);
41         else{
42             printAllCore(str1, str2, dp, i-1, j, result, tmp);
43             printAllCore(str1, str2, dp, i, j-1, result, tmp);
44         }
45     }
46 }
47 void printAll(string str1, string str2, int **dp)
48 {
49     vector<string> result;
50     string tmp;
51     printAllCore(str1, str2, dp, str1.size(), str2.size(), result, tmp);
52     for(int i=0; i<result.size(); ++i)cout<<result[i]<<endl;
53 }
54 int LCSeq(string str1, string str2)
55 {
56     if(str1.size()==0 || str2.size()==0)return 0;
57     //分配内存dp[str1.size()+1][str2.size()+1]; 
58     int **dp=new int*[str1.size()+1];
59     for(int i=0; i<str1.size()+1; ++i)
60     {
61         dp[i]=new int[str2.size()+1];
62     }
63     //初始化数组
64     //dp[i][0]=0 and dp[0][j]=0;
65     for(int i=0; i<str1.size()+1; ++i)dp[i][0]=0;
66     for(int j=0; j<str2.size()+1; ++j)dp[0][j]=0;
67     //填数组
68     for(int i=1; i<str1.size()+1; ++i)
69     {
70         for(int j=1; j<str2.size()+1; ++j)
71         {
72             if(str1[i-1]==str2[j-1])dp[i][j]=dp[i-1][j-1]+1;//注意此处应减1,因为dp二维数组的长和宽都加了1 
73             else dp[i][j]=max(dp[i-1][j], dp[i][j-1]);
74         }    
75     }
76     int result=dp[str1.size()][str2.size()];
77     //printOne(str1, str2, dp);//打印一个
78     printAll(str1, str2, dp);//打印所有
79     //释放空间
80     for(int i=0; i<str1.size()+1; ++i)
81     {
82         delete [] dp[i];    
83     }
84     delete [] dp;
85     return result;
86 }

 

2、最长公共子串(longest common substring)

  状态转移方程:

  实现代码:

 1 int LCSub(string str1, string str2)
 2 {
 3     if(str1.size()==0 || str2.size()==0)return 0;
 4     //分配内存dp[str1.size()+1][str2.size()+1]; 
 5     int **dp=new int*[str1.size()+1];
 6     for(int i=0; i<str1.size()+1; ++i)
 7     {
 8         dp[i]=new int[str2.size()+1];
 9     }
10     //初始化数组
11     //dp[i][0]=0 and dp[0][j]=0;
12     for(int i=0; i<str1.size()+1; ++i)dp[i][0]=0;
13     for(int j=0; j<str2.size()+1; ++j)dp[0][j]=0;
14     //填数组
15     for(int i=1; i<str1.size()+1; ++i)
16     {
17         for(int j=1; j<str2.size()+1; ++j)
18         {
19             if(str1[i-1]==str2[j-1])dp[i][j]=dp[i-1][j-1]+1;//注意此处应减1,因为dp二维数组的长和宽都加了1 
20             else dp[i][j]=0;
21         }    
22     }
23     int max=0;
24     for(int i=0; i<str1.size()+1; ++i)
25     {
26         for(int j=0; j<str2.size()+1; ++j)
27         {
28             if(dp[i][j]>max)max=dp[i][j];
29         }
30     }
31     //释放空间
32     for(int i=0; i<str1.size()+1; ++i)
33     {
34         delete [] dp[i];    
35     }
36     delete [] dp;
37     return max;
38 }

   同样地,如果想打印最长公共子串,也是可以的,也有两种打印方式,分别打印一个和所有个:

 1 void printOne(string str1, string str2, int **dp, int max)
 2 {
 3     for(int i=0; i<str1.size()+1; ++i)
 4     {
 5         for(int j=0; j<str2.size()+1; ++j)
 6         {
 7             if(dp[i][j]==max)
 8             {
 9                 int m=i, n=j;
10                 string tmp;
11                 while(m-1>=0 && n-1>=0 && str1[m-1]==str2[n-1])
12                 {
13                     tmp=str1[m-1]+tmp;
14                     --m;
15                     --n;
16                 }
17                 cout<<tmp<<endl;
18                 return;
19             }
20         }
21     }
22 }
23 bool isExist(vector<string> &result, string tmp)
24 {
25     for(int i=0; i<result.size(); ++i)
26     {
27         if(result[i]==tmp)return true;
28     }
29     return false;
30 }
31 void printAll(string str1, string str2, int **dp, int max)
32 {
33     vector<string> result;
34     for(int i=0; i<str1.size()+1; ++i)
35     {
36         for(int j=0; j<str2.size()+1; ++j)
37         {
38             if(dp[i][j]==max)
39             {
40                 int m=i, n=j;
41                 string tmp;
42                 while(m-1>=0 && n-1>=0 && str1[m-1]==str2[n-1])
43                 {
44                     tmp=str1[m-1]+tmp;
45                     --m;
46                     --n;
47                 }
48                 if(!isExist(result, tmp))result.push_back(tmp);//去重 
49             }
50         }
51     }
52     for(int i=0; i<result.size(); ++i)cout<<result[i]<<endl;
53 }
54 int LCSub(string str1, string str2)
55 {
56     if(str1.size()==0 || str2.size()==0)return 0;
57     //分配内存dp[str1.size()+1][str2.size()+1]; 
58     int **dp=new int*[str1.size()+1];
59     for(int i=0; i<str1.size()+1; ++i)
60     {
61         dp[i]=new int[str2.size()+1];
62     }
63     //初始化数组
64     //dp[i][0]=0 and dp[0][j]=0;
65     for(int i=0; i<str1.size()+1; ++i)dp[i][0]=0;
66     for(int j=0; j<str2.size()+1; ++j)dp[0][j]=0;
67     //填数组
68     for(int i=1; i<str1.size()+1; ++i)
69     {
70         for(int j=1; j<str2.size()+1; ++j)
71         {
72             if(str1[i-1]==str2[j-1])dp[i][j]=dp[i-1][j-1]+1;//注意此处应减1,因为dp二维数组的长和宽都加了1 
73             else dp[i][j]=0;
74         }    
75     }
76     int max=0;
77     for(int i=0; i<str1.size()+1; ++i)
78     {
79         for(int j=0; j<str2.size()+1; ++j)
80         {
81             if(dp[i][j]>max)max=dp[i][j];
82         }
83     }
84     //printOne(str1, str2, dp, max);//打印一个
85     printAll(str1, str2, dp, max);//打印所有
86     //释放空间
87     for(int i=0; i<str1.size()+1; ++i)
88     {
89         delete [] dp[i];    
90     }
91     delete [] dp;
92     return max;
93 }

 

3、最小编辑距离

  之前做项目的时候用到过最小编辑距离算法,这里也总结一下。

  状态转移方程:

  实现代码:

 1 int minEditDistance(string str1, string str2)
 2 {
 3     if(str1.size()==0)return str2.size();
 4     if(str2.size()==0)return str1.size();
 5     //分配内存dp[str1.size()+1][str2.size()+1]; 
 6     int **dp=new int*[str1.size()+1];
 7     for(int i=0; i<str1.size()+1; ++i)
 8     {
 9         dp[i]=new int[str2.size()+1];
10     }
11     //初始化数组
12     //dp[0][0]=0;
13     //dp[i][0]=i and dp[0][j]=j;
14     for(int i=0; i<str1.size()+1; ++i)dp[i][0]=i;
15     for(int j=0; j<str2.size()+1; ++j)dp[0][j]=j;
16     //填数组
17     for(int i=1; i<str1.size()+1; ++i)
18     {
19         for(int j=1; j<str2.size()+1; ++j)
20         {
21             int flag=0;
22             if(str1[i-1]!=str2[j-1])flag=1;//注意此处应减1,因为dp二维数组的长和宽都加了1 
23             dp[i][j]=min(dp[i-1][j]+1, dp[i][j-1]+1, dp[i-1][j-1]+flag);
24         }    
25     }
26     int result=dp[str1.size()][str2.size()];
27     //释放空间
28     for(int i=0; i<str1.size()+1; ++i)
29     {
30         delete [] dp[i];    
31     }
32     delete [] dp;
33     return result;
34 }

 

posted @ 2018-03-11 16:38  jeysin  阅读(829)  评论(0编辑  收藏  举报