/* 最长公共子序列: 假定,A=a 1 a 2 …a n 是字母表∑上的一个字符序列,如果存在 ∑上的另外一个字符序列S=c 1 c 2 …c j ,使得对所有的k, k=1,2,…,j,有c k =a ik (其中,1≤ik≤n),是字符序列A的一个 下标递增序列,则称字符序列S是A的子序列。 如果∑={x,y,x}, ∑上的字符序列是A=xyzyxzxz,则xxx是A的 一个长度为3的子序列。该子序列中的字符,对应于A的下 标是1,5,7。而xzyzx是A的长度为5的子序列,对应于A的下 标是1,3,4,6,7. 分析: 令序列A=a1a2...an和B=b1b2...bm,记A=a1a2...ak为A中最前面连续k个字符的子序列, 序列A和序列B的最长公共子序列性质: 1)若an = bm,序列Sk = c1c2...ck是A和B的最长公共子序列,有an = bm = ck, 且序列Sk-1=c1c2...ck-1是序列An-1和Bm-1的长度为k-1的最长公共子序列 2)若an != bm,且an != ck,那么应该将序列A最后字符an排除,用An-1与B进行比较 Sk 是An-1与Bm的长度为k的最长公共子序列 3)若an != bm,且bm != ck,Sk是An与Bm-1的长度为k最长公共子序列 划分状态: 第一阶段:计算A1和Bj的LCS长度L1,j ,j = 1,2,...,m 第二阶段: 计算A2和Bj的LCS长度L2,j ,j = 1,2,...,m ... 第n阶段 :计算An和Bj的LCS长度Ln,j ,j = 1,2,...,m 第n阶段中的Ln,m是序列An和Bm的最长公共子序列的长度 搜索状态: 设置二维状态数组si,j。 si,j = 1,ai = bj si,j = 2,ai != bj,且Li-1,j >= Li,j-1,序列A优先 si,j = 3,ai != bj,且Li-1,j < Li,j-1,序列B优先 设Ln,m=k,Sk=c1c2...ck是An和Bm的长度为k的最长公共子序列,搜索过程从状态字Sn,m开始。 若sn,m=1,an = bm -> 搜索sn-1,m-1 若sn,m=2,an !=bm,且Ln-1,m >= Ln,m-1,序列A优先 -> 搜索sn-1,m 若sn,m=3, sn,m-1 搜索起点:i = n,j = m 搜索终止:i = 0 或 j = 0 先通过初始状态,从上向下,从左向右填表,填表利用递推 然后把表填完,从最后再开始搜索 递推和动态规划的结合 思想: 1)记Ln,m为序列An和Bm的最长公共子序列的长度,则为Li,j为序列Ai和Bj的长度。 2)状态迁移方程:Ln,m = {Ln-1,m-1 + 1,an = bm,n > 0 ,j >0 {max{Ln-1,m ,Ln,m-1},an!= bm 3)边界: Li,0 = L0,j = L0,0 = 0,1<=i<=n,1<=j<=m 4)目标状态:Ln,m 算法过程: 1填表: L[i][j] = {L[i-1][j-1] + 1,ai = bj,1<=i<=n,1<=j<=m, {max(L[i-1][j] , L[i][j-1]), ai != bj,1<=i<=n,1<=j<=m {0,i = 0 或者 j = 0 2搜索: S[n][m] = {1,an = bm {2,an != bm && L[i-1][j] >= L[i][j-1] {3,an != bm && L[i-1][j] < L[i][j-1] 3 n = 0 或 m = 0算法结束 输入: xyxzyxyzzy xzyzxyzxyzxy xyzyxzxz xzyxxyzx xzyxxyzx xyzyxzxz(先后顺序不同,答案不同) 输出: 8 xyxzxyzy 6 xzyxzx 6 xzyxxz */ #include <iostream> #include <string> #include <string.h> #include <vector> using namespace std; const int MAXSIZE = 100; int max(int a,int b) { return a > b ? a : b; } int LCS(string& sA,string& sB,vector<char>& vecLCS) { //鲁棒性 if(sA.empty() || sB.empty()) { return 0; } int n = sA.size(); int m = sB.size(); //设定递推数组的初始值 int iLCSArr[MAXSIZE][MAXSIZE]; //memset(iLCSArr,0,sizeof(iLCSArr)); //自顶向下递推 for(int i = 0 ; i < n ; i++) { for(int j = 0 ; j < m ; j++) { if(i != 0 && j != 0) { if(sA.at(i) != sB.at(j)) { iLCSArr[i][j] = max(iLCSArr[i-1][j],iLCSArr[i][j-1]); } else { iLCSArr[i][j] = iLCSArr[i-1][j-1] + 1; } } else { iLCSArr[i][j] = 0; } } } //测试,打印长度表 cout << "长度表:" << endl; for(int i = 0 ; i < n ; i++) { for(int j = 0 ; j < m ;j++) { cout << iLCSArr[i][j]; } cout << endl; } //填表 int iMatrix[MAXSIZE][MAXSIZE]; for(int i = 0 ; i < n ; i++) { for(int j = 0 ; j < m ; j++) { if(sA[i] == sB[j]) { iMatrix[i][j] = 1; } else { //序列A优先 if(iLCSArr[i-1][j] >= iLCSArr[i][j-1]) { iMatrix[i][j] = 2; } //序列B优先 else { iMatrix[i][j] = 3; } } } } //测试,打印填表 cout << "\n状态表:" << endl; for(int i = 0 ; i < n ; i++) { for(int j = 0 ; j < m ;j++) { cout << iMatrix[i][j]; } cout << endl; } cout << endl; //搜索 int iRet = 0; for(int i = n -1 ; i >= 0 ;) { for(int j = m - 1 ; j >= 0 ; ) { //相等 if(iMatrix[i][j] == 1) { iRet++; vecLCS.push_back(sA[i]); i--; j--; } //序列A优先,直接向上走,i-- else if(iMatrix[i][j] == 2) { i--; } //序列B优先,直接向左走,j-- else if(iMatrix[i][j] == 3) { j--; } } } return iRet; } void process() { string sA,sB; vector<char> vecLCS; cout << "请输入两个字符串:" << endl; while(cin >> sA >> sB) { vecLCS.clear(); printf("%d\n",LCS(sA,sB,vecLCS)); for(int i = vecLCS.size() - 1; i>= 0 ;i-- ) { cout << vecLCS.at(i); } cout << endl; } } int main(int argc,char* argv[]) { process(); getchar(); return 0; }