首先需要说一下的是,什么是公共子序列。子串的概念就是,一个字符串中连续的一部分, 而 子序列,可以是不连续的。
•一个给定序列的子序列是在该序列中删去若干元素后得到的序列。
•给定两个序列X和Y,当另一序列Z既是X的子序列又是Y的子序列时,称Z是序列X和Y的公共子序列。
•最长公共子序列:公共子序列中长度最长的子序列。
【穷举法】
•对于每一个Xm的子序列,验证它是否是Yn的子序列.
•Xm有2m个子序列
•每个子序列需要o(n)的时间来验证它是否是Yn的子序列.
–从Yn的第一个字母开始扫描下去,如果不是则从第二个开始
•运行时间: o(2^n*2^m)
【动态规划】
•两个字符串【横向,纵向排列】
ABDTFGHFEABADG
BTHEBDCEFG
A B DT F G H F EA B A D G
B 0 1 1 1 1 1 1 1 1 1 1 1 1 1
T 0 1 1 2 2 2 2 2 2 2 2 2 2 2
H 0 1 1 2 2 2 3 3 3 3 3 3 3 3
E 0 1 1 2 2 2 3 3 4 4 4 4 4 4
B 0 1 1 2 2 2 3 3 4 4 5 5 5 5
D 0 1 2 2 2 2 3 3 4 4 5 5 6 6
C 0 1 2 2 2 2 3 3 4 4 5 5 6 6
E 0 1 2 2 2 2 3 3 4 4 5 5 6 6
F 0 1 2 2 3 3 3 4 4 4 5 5 6 6
G 0 1 2 2 3 4 4 4 4 4 5 5 6 7
T 0 1 1 2 2 2 2 2 2 2 2 2 2 2
H 0 1 1 2 2 2 3 3 3 3 3 3 3 3
E 0 1 1 2 2 2 3 3 4 4 4 4 4 4
B 0 1 1 2 2 2 3 3 4 4 5 5 5 5
D 0 1 2 2 2 2 3 3 4 4 5 5 6 6
C 0 1 2 2 2 2 3 3 4 4 5 5 6 6
E 0 1 2 2 2 2 3 3 4 4 5 5 6 6
F 0 1 2 2 3 3 3 4 4 4 5 5 6 6
G 0 1 2 2 3 4 4 4 4 4 5 5 6 7
状态迁移方程:
c[i][j] 是用来保存截止到X[i],Y[j]的时候的最长公共子序列的长度。
c[i][j] =0, i = j =0;
c[i][j] = c[i-1][j-1]+1 , i>0,j>0 && Xi = Yj
c[i][j] = max(c[i-1][j],c[i][j-1]), i>0,j>0 && Xi ≠ Yj
#include<iostream> #include<string> #include<algorithm> using namespace std; const int maxn = 1000; string str1,str2; int c[maxn][maxn]; int n;int m; void solve() { for(int i = 0;i<n;i++) { for(int j = 0;j<m;j++) { if(str1[i] ==str2[j]) c[i+1][j+1] = c[i][j] + 1; else c[i+1][j+1] = max(c[i+1][j],c[i][j+1]); } } } int main() { cin>>str1>>str2; n = str1.length(); m = str2.length(); solve(); cout<<c[n][m]<<endl; return 0; }
后来仔细想想,没那么复杂其实也,只要自己肯动脑子……
昨天晚上那个心急啊,想想该如何输出其中的一个最大公共子序列呢……
矩阵 b[i, j]:存放当前的路径,也就是说c[i][j]是如何得来的。
如果c[i][j] = c[i-1][j-1] 也就是对角线的关系;b[i][j] 标记为 '↖' b[i][j] = 'S';
如果c[i][j] = c[i][j-1] 也就是左右的关系 ,b[i][j] 标记为 '←' b[i][j] = 'L';
如果c[i][j] = c[i-1][j] 也就是上下的关系,b[i][j] 标记为 '↑' b[i][j] = 'U';
例如:
B D C A B A
A U U U S L S
B S L L U S L
C U U S L U U
B S U U U S L
D U S U U U U
A U U U S U S
B S U U U S U
从最后一个开始查找 U→S→U→S→L→S→L→S其中S对应的str1的元素就是其中的一条最长公共子子序列。
使用递归的办法,遇到一个S就压入栈……
#include<iostream> #include<string> #include<algorithm> using namespace std; const int maxn = 1000; string str1,str2; int c[maxn][maxn]; char b[maxn][maxn]; int n;int m; void printLCS(int i, int j) { if(i==0 || j==0) return; if(b[i][j] == 'S') { printLCS(i-1, j-1); cout<<str1[i-1]<<' '; } else { if(b[i][j] == 'U') printLCS(i-1, j); else printLCS(i, j-1); } } void solve() { for(int i = 0;i<n;i++) { for(int j = 0;j<m;j++) { if(str1[i] ==str2[j]) { c[i+1][j+1] = c[i][j] + 1; b[i+1][j+1] = 'S'; } else if(c[i+1][j]>c[i][j+1]) { c[i+1][j+1] = c[i+1][j]; b[i+1][j+1] = 'L'; } else { c[i+1][j+1] = c[i][j+1]; b[i+1][j+1] = 'U'; } } } } int main() { cin>>str1>>str2; n = str1.length(); m = str2.length(); solve(); cout<<c[n][m]<<endl; for(int i = 1;i<=n;i++) { for(int j = 1;j<=m;j++) cout<<c[i][j]<<' '; cout<<endl; } for(int i = 1;i<=n;i++) { for(int j = 1;j<=m;j++) cout<<b[i][j]<<' '; cout<<endl; } printLCS(n,m); return 0; }