最长公共子序列,这个大家都熟悉,规则不说了
解决这类问题,经典的算法是动态规划方法。这种方法在求解组合最优问题常用。
使用这种方法的算法不是递归调用自身,而是采取自底向下的方式递推求值,并把中间结果存储起来以便以后用来计算所需要求的解
为求最长公共子序列,首先寻找一个求最长公共子序列长度的递推公式
令A=a1,a2,a3,...an和B=b1,b2,b3,...bn。 L[i][j]表示序列A和B上的最长公共子序列的长度。
我们很容易分析出,如果i=0或j=0,那么L[i][j] = 0;
从上面的分析我们得出递推公式
= 0, 若i=0 或 j=0
L[i][j] = L[i-1][j-1]+1 若i>0,j>0 和 a[i] = b[j]
= max(L[i-1][j],L[i][j-1]) 若i>0,j>0 和 a[i] != b[j]
程序:
#include <iostream>
#include <fstream>
#include <string>
#include <vector>
#include <algorithm>
using namespace std;
class MyMaxCommonString
{
string a;
string b;
vector<vector<int> >L;
public:
MyMaxCommonString(string s1,string s2) : a(s1),b(s2),L(s1.length()+1,vector<int>(s2.length()+1,0))
{
;
}
int GetMaxLength()
{
int row = L.size();
int col = L[0].size();
return L[row-1][col-1];
}
string GetMaxCommonString()
{
string s = "";
int maxLen = GetMaxLength();
vector<int>::iterator it;
if(maxLen > 0)
{
for(int i = 0 ;i < L.size(); i ++)
{
it = find(L[i].begin(),L[i].end(),maxLen);
if(it != L[i].end())
break;
}
int row = i;
int col = it - L[i].begin();
s += a.substr(row-1,1);
int start = maxLen - 1;
int end = start;
while(start >= 1)
{
row --,col --;
end = L[row][col];
if(end != start)
{
s += a.substr(row,1);
start --;
}
}
reverse(s.begin(),s.end());
}
return s;
}
void Process()
{
for(int i = 1; i < L.size() ;i ++)
{
for(int j = 1; j <L[0].size(); j ++)
{
if(a.at(i-1) == b.at(j-1))
{
L[i][j] = L[i-1][j-1] + 1;
}
else
L[i][j] = _MAX(L[i][j-1],L[i-1][j]);
}
}
}
void Show(ostream &os)
{
for(int i = 0 ;i < L.size(); i++)
{
copy(L[i].begin(),L[i].end(),ostream_iterator<int>(os,"\t"));
os << endl;
}
}
};
int main()
{
string s1 = "xyxxzxyzxy";
string s2 = "zxzyyzxxyxxz";
MyMaxCommonString m(s1,s2);
cout << "string1 : " << s1 << endl;
cout << "string2 : " << s2 << endl;
m.Process();
m.Show(cout);
int maxLen = m.GetMaxLength();
string maxstr = m.GetMaxCommonString();
cout << "Max common string length: " << maxLen << endl;
cout << "Max common string is : " << maxstr << endl;
return 0;
}
递推二维表:
红色代表下标,黑色代表数值,即最长公共子序列长度,蓝色代表求最长公共子序列串时的回朔路线