emmmmm,今天去面试没面试到,简历交了被劝退…..
最近毕业季,查重的时候想到了一个算法,就是最长子序列,也就是查询集合A与集合B的交集C且C中的元素顺序严格按照在A与B中的顺序,但不要求元素是连续的。
以前在上课的时候,不认真听,照着书上的伪代码巧了一个迭代方式实现的,所以今天来写一个递归的。
递归式:
c[i][j]= 0 when i=0||j=0
c[i][j]=c[i-1][j-1]+1 when str1[i]=str[j]
c[i][j]=max(c[i-1][j],c[i][j-1]) when str1[i]!=str[j]
i与j是两个字符串的下标,第一个式子是当下标为0的时候(假设字符串下标是从1开始的前提),也就没有字符串比较,长度为0,相当于一个边界条件。当str[i]=str[j],就长度就+1,然后缩短下标范围去判断i-1与j-1位置的字符,当str1[i]!=str2[j]的时候,这时候就要判断最长子序列是在str1[1~i]与str2[1~j-1]中,还是str1[1~i-1]与str2[1~j]中,两者中子序列长度最长的,就意味着最长子序列就在那,所以我们就要用一个数组来记录程序在递归或者迭代的过程中进行的抉择,是选择c[i-1][j]还是c[i][j-1]。
如果想找到字符串中多个一样和长度的最长子串,我觉得可以试一下用回溯法,应该就能找出=v=
代码:
int Lcs(string &s1,string &s2, int i, int j,int** &arr) {
if (arr == nullptr) {
arr = new int*[s1.length()+1];
for (int i = 0; i <= s1.length(); i++) {
arr[i] = new int[s2.length()+1];
}
for (int i = 0; i <= s1.length(); i++) {
arr[i][0] = 0;
for (int j = 0; j <= s2.length(); j++) {
arr[0][j] = 0;
if (i >= 1 && j >= 1) {
arr[i][j] = -1;
}
}
}
}
if (i == 0 || j == 0) {
arr[i][j] = 0;
return arr[i][j];
}
if (arr[i][j] != -1) {
return arr[i][j];
}
if (s1[i-1] == s2[j-1]){
arr[i][j]= Lcs(s1, s2, i - 1, j - 1,arr) + 1;
}
else {
arr[i][j]= max(Lcs(s1, s2, i - 1, j,arr), Lcs(s1, s2, i, j - 1,arr));
}
return arr[i][j];
}
string findstr(string &s1, string &s2, int **arr) {
if (arr == nullptr) {
return "";
}
int slen1 = s1.length();
int slen2 = s2.length();
string result = "";
for (int i = slen1; ;) {
for (int j = slen2; ;) {
if (arr[i][j]==0) {
return result;
}
if (s1[i-1] == s2[j-1]) {
result += s1[i - 1];
--i, --j;
}
else {
if (arr[i - 1][j] < arr[i][j - 1]) {
i--;
}
else {
j--;
}
}
}
}
}
int main()
{
string s1 = "ABCBDAB", s2 = "BDCABA";
const int slen1 = s1.length(), slen2 = s2.length();
int **arr = nullptr;
cout << "长度为" << Lcs(s1, s2, slen1, slen2,arr) << endl;
for (int i = 0; i <= slen1; i++) {
for (int j = 0; j <= slen2; j++)
{
cout << arr[i][j] << " ";
}
cout << endl;
}
cout << findstr(s1, s2, arr);
for (int i = 0; i <= slen1; i++) {
delete[] arr[i];
}
delete[] arr;
return 0;
}
结果: