最长公共子序列和最长公共子串
最长公共子序列:
给定一个序列X=<x1,x2,x3,x4...,xm>,另一个序列Z=<z1,z2,z3,z4...,zk>,若存在一个严格递增的X的下标序列<i1,i2,i3,...,ik>对所有的1,2,3,...,k,都满足x(ik)=zk,则称Z是X的子序列
比如Z=<B,C,D,B>是X=<A,B,C,B,D,A,B>的子序列
对于如下的两个字符串 她的最长公共子序列是2345.
string s1 = "1AB2345CD";
string s2 = "2345EF";
最长公共子序列的长度是固定的,但是得到的子序列不一定是一样的。
例如:
S1=2FE45 S2=24EF5 长度为3 但是 子序列可以是 245 也可以是2F5
理解了上诉的题目,我们可以知道,对于S1,S2的到的公共子序列S3可以得到如下的定义:
我们假设S3就是我们得到的那个子序列,n是s1的长度,m是s2的长度,k是s3的长度
1.如果 s1[n]=s2[m] 且s3[k]=s1[n]=s2[m],那么s3[k]一定是s2[m]和s1[n] 得到的子序列
2.如果 s1[n]!=s2[m] 且s3[k]!=s1[n] 那么s3[k] 一定是s2[m]和s1[n-1] 得到的子序列
2.如果 s1[n]!=s2[m] 且s3[k]!=s2[m] 那么s3[k] 一定是s2[m-1]和s1[n] 得到的子序列
如此循环,直到我们遍历完整个字符串,我们就可以得到结果。
因此我们就可以写出如下的递推方程:
根据如此,我们就可以写出如下的代码。
int a = 0;
int LSClength(string str1, string str2, int i, int j)
{
if (i<0||j<0)
return 0;
a++;
if (str1[i] == str2[j])
{
return LSClength(str1, str2, i - 1, j - 1) + 1;
}
return max(LSClength(str1, str2, i, j - 1), LSClength(str1, str2, i-1, j ));
}
对于a,和本题没有关系,只是为了比较统计一下,一共计算了多少次:
得到 结果为4,一共计算了 1946次。
但是对于动态规划来说,不仅要找到递推方程,更要用二维数组来保存,这样可以减少重复计算的过程,如下就是填表的过程:
首先我们先申请一个二维数组,将横坐标等于0,和纵坐标等于0的先空下来,是因为方便计算,边界值更好处理。
根据我们的递推方程可以得到如下的填写规则:
首先我们从横坐标为1的哪一行开始填写,先比较进行比较,如果不相等, 因此这个位置填写的一个是 左边和上边更大的那个值,如果相等的时候,我们就填写的是左上角的值在+1。
这样我们可以写出如下的代码:
int b = 0;
int LSClength(string str1, string str2, int i, int j, vector<vector<int>> &vec)
{
b++;
if (i < 0 || j < 0)
return 0;
if (vec[i][j]>0)
return vec[i][j];
if (str1[i] == str2[j])
vec[i ][j ] = LSClength(str1, str2, i - 1, j - 1, vec) + 1;
else
vec[i ][j] = max(LSClength(str1, str2, i - 1, j, vec), LSClength(str1, str2, i, j - 1, vec));
return vec[i ][j];
}
b的作用和上面的a是一样的,得到了如下的结果:
得到的结果也是 4,计算了469次,并且得到了填好表的数组。
这时候,我们就可以得到我们的答案了,但是如果我们不仅需要得到长度,还想要得到字符串,这时候,我们就需要在定义一个数组,如果,当我们的 s1[n]=s2[m]的时候,就填写1,如果不相等的时候,左移就是2,上移就是3得到如下的数组。
如此我们就可以得到结果。
/LSC 最长公共子序列
void print_vector(vector<vector<int>> &c) //打印数组
{
for (int i = 0; i<c.size(); i++)
{
for (int j = 0; j < c[i].size(); j++)
{
cout << setw(5) << c[i][j] << " ";
}
cout << endl;
}
}
int LSClength(string str1, string str2, int i, int j, vector<vector<int>> &vec, vector<vector<int>> &vrc) //递归求长度的
{
if (i < 0 || j < 0)
return 0;
if (vec[i][j] > 0) return vec[i][j];
else
{
if (str1[i] == str2[j])
{
vec[i][j] = LSClength(str1, str2, i - 1, j - 1, vec,vrc) + 1;
vrc[i][j] = 1;
}
else
{
int n1 = LSClength(str1, str2, i - 1, j, vec, vrc);
int n2 = LSClength(str1, str2, i, j - 1, vec, vrc);
if (n1 > n2)
{
vec[i][j] = n1;
vrc[i][j] = 2;
}
else
{
vec[i][j] = n2;
vrc[i][j] = 3;
}
}
}
return vec[i][j];
}
int NiceLSClength(string str1, string str2, int i, int j, vector<vector<int>> &vec, vector<vector<int>> &vrc) //非递归求长度
{
if (str1[0] == str2[0])
{
vec[0][0] = 1;
vrc[0][0] = 1;
}
for (int a = 1; a <= i; a++)
{
if (str1[a] == str2[0])
{
vec[a][0] = 1;
vrc[a][0] = 1;
}
else
{
vec[a][0] = vec[a - 1][0];
vrc[a][0] = 2;
}
}
for (int b = 1; b <= j; b++)
{
if (str1[0] == str2[b])
{
vec[0][b] = 1;
vrc[0][b] = 1;
}
else
{
vec[0][b] = vec[0][b - 1];
vrc[0][b] = 3;
}
}
for (int a = 1; a <= i; a++)
{
for (int b = 1; b <= j; b++)
{
if (str1[a] == str2[b])
{
vec[a][b] = vec[a - 1][b - 1] + 1;
vrc[a][b] = 1;
}
else
{
if (vec[a - 1][b] > vec[a][b - 1])
{
vec[a][b] = vec[a - 1][b];
vrc[a][b] = 2;
}
else
{
vec[a][b] = vec[a][b - 1];
vrc[a][b] = 3;
}
}
}
}
return vec[i][j];
}
void print_stack(stack<char> &st)
{
while (!st.empty())
{
cout << st.top();
st.pop();
}
cout << endl;
return;
}
void Stanck_vector(int i, int j, stack<char>&st, string X, vector<vector<int>> &s) //通过数组得到 子串的, 将其放进栈里面
{
if (i<0 || j<0)
{
print_stack(st);
return;
}
int pos = s[i][j];
if (pos == 1)
{
st.push(X[i]);
//cout << X[i];
Stanck_vector(i - 1, j - 1, st, X, s);
}
else
{
if (pos == 2)
{
Stanck_vector(i - 1, j, st, X, s);
}
else
{
Stanck_vector(i, j - 1, st, X, s);
}
}
}
最长公共子串:
有两个字符串,这两个字符串可能会存在公共的部分,如字符串"abcdef" 和字符串"defg",这两个字符串之间有共同的字符串,“d”,“e”,“f”,“de”,“ef”,“def” 等。最长的公共子串就是"def"。
和上上题不同的是,我们的子串在原字符串中必须是连续的。
其他的和上题是一样的,我们很快很能得出如下的递推方程:
得到的代码如下:
void print_vector(vector<vector<int>> &c) //打印数组
{
for (int i = 0; i<c.size(); i++)
{
for (int j = 0; j < c[i].size(); j++)
{
cout << setw(5) << c[i][j] << " ";
}
cout << endl<<endl;
}
}
string LSClength(string s1, string s2, int n1, int n2, vector<vector<int>>& vec) //求子串的
{
int big = 0; //最大长度
int end = 0; //结束位置
vec[0][0] = 0;
for (int i = 1; i <= n1; i++)
{
for (int j = 1; j <= n2; j++)
{
if (s1[i] == s2[j])
{
vec[i][j] = vec[i - 1][j - 1] + 1;
if (big < vec[i][j])
{
big = vec[i][j];
end = i;
}
}
}
}
if (big == 0)
return "-1";
string s3 = s1.substr(end - big + 1, big);
print_vector(vec);
return s3;
}
string LCS(string str1, string str2)
{
// write code here
int n1 = str1.size();
int n2 = str2.size();
if (n1 == 0 || n2 == 0)
return "-1";
str1 = "#" + str1;
str2 = "#" + str2;
vector<vector<int>>vec;
vec.resize(n1 + 1);
for (int i = 0; i<n1 + 1; i++)
{
vec[i].resize(n2 + 1);
}
return LSClength(str1, str2, n1, n2, vec);
}
int main()
{
string s1 = "acbcbcef";
string s2 = "abcbced";
cout << LCS(s1, s2) << endl;
return 0;
}
在这一个中很多的子串,我们需要找到最大的那个。