#include<iostream>
#include<string>
#include<vector>
using namespace std;
//找两个子串str1,str2的最长公共子串substr
void findLongestSubString(string &str1, string &str2, string &substr){
if (str1.empty() || str2.empty()){
return;
}
//定义二维数组lengt[str.size() + 1][str2.size() + 1]来保存最长公共子串的长度
vector<vector<int> > length(str1.size() + 1, vector<int> (str2.size() + 1, 0) );
//定义搜索状态二维数组state[str.size() + 1][str2.size() + 1],为了后面寻找最长公共子串元素
vector<vector<int> > state(str1.size() + 1, vector<int> (str2.size() + 1, 0) );
//下面就是运用动态规划求最长公共子串
for(size_t i = 1; i <= str1.size(); i++){
for(size_t j = 1; j <= str2.size(); j++){
if(str1[i - 1] == str2[j - 1]){
length[i][j] = 1 + length[i -1][j - 1];
state[i][j] = 1;
}
else{
length[i][j] = (length[i-1][j] >= length[i][j-1] ? length[i-1][j] : length[i][j-1]);
if (length[i-1][j] >= length[i][j-1]){
state[i][j] = 2;
}
else{
state[i][j] = 3;
}
}
}
}
for (size_t i = str1.size(), j = str2.size(); i > 0 && j > 0;){
if (state[i][j] == 1){
substr.push_back(str1[i-1]);
i--;
j--;
}
else if (state[i][j] == 2){
i--;
}
else {
j--;
}
}
return;
}
//翻转字符串
void reverseString(string &str){
if (str.empty()){
return;
}
for (size_t i = 0, j = str.size() - 1; i < j; i++, j--){
str[i] = str[i] ^ str[j];
str[j] = str[j] ^ str[i];
str[i] = str[j] ^ str[i];
}
return;
}
int main(void){
string str1, str2, substr;
//输入字符串str1,str2
cin >> str1 >> str2;
findLongestSubString(str1, str2, substr);
reverseString(substr);
cout << "string 1: " << str1 << endl << "string 2: " << str2 << endl << "substring: " << substr << endl;
return 0;
}
给定字符串X=(X1,X2,...,Xm),Y= (Y1,Y2,...,Yn)。求字符串X和Y的最长公共子序列。
首先分析字符串X和Y的最后一个字符Xm和Yn:
1、如果Xm == Yn,那么Xm一定是字符串X和Y的最长公共子串中的最后一个元素。接下来只要分别求X的前m-1个元素组成的子串(X1,X2,...,Xm-1)和Y的前n-1个元素组成的子串(Y1,Y2,...,Yn-1)的最长公共子串。
2、如果Xm != Yn,那么X和Y的最长公共子串要么是(X1,X2,...,Xm)与(Y1,Y2,...,Yn-1)的最长公共子串;要么是(X1,X2,...,Xm-1)与(Y1,Y2,...,Yn)的最长公共子串。两者中选长度较长的。
下面列递推方程:
设length[ i ][ j ]表示字符串X的前i个字符与字符串Y的前j个字符的最长公共子串的长度。那么有上面的分析我们有:
length[0][0] = length[0][j] = length[i][0] = 0; (1 <= i <= m, 1 <= j <= n)
if (Xi == Yj) length[ i ][ j ] = length [i - 1][j - 1] + 1; (i >= 1, j>=1)
if (Xi != Yj) length[ i ][ j ] = max(length[ i - 1][ j ], lenght[ i ][j - 1]); (i >= 1, j >= 1)
同时为了得到序列X和Y的最长公共子序列,设二维表state[m+1][n+1],其中state[i][j]表示在计算length[i][j]的过程中的搜索状态:
if(Xi == Yj) state[i][j] = 1 //说明X的前i个元素和Y的前j个元素的最长公共子序列包含Xi,也就是X和Y的最长公共子序列包含Xi
if(Xi != Yj && length[i - 1][j] >= length[i][j - 1]) state[i][j] = 2; //说明X的前i个元素和Y的前j个元素的最长公共子序列是由X的前i-1个元素和Y的前j个元素的最长公共子序列得到的。同时说明他们的最长公共子序列中不包含元素Xi。
if(Xi != Yj && length[i - 1][j] < length[i][j - 1]) state[i][j ] = 3; //说明X的前i个元素和Y的前j个元素的最长公共子序列是由X的前i个元素和Y的前j-1个元素的最长公共子序列得到的。同时说明他们的最长公共子序列中不包含元素Yj。
那么在用状态表搜索最长公共子序列时,如果state[i][j] == 1,表明Xi = Yj,元素Xi为最长公共子序列的元素,将这个元素存入子串中。下一个搜索方向为state[i - 1][ j - 1]。
如果state[i][j] == 2,表明Xi != Yj,length[i - 1][j] >= length[i][j - 1],并且最长公共子序列的元素在X的前i-1和Y的前j个元素的最长公共子序列中,那么继续搜素state[i-1][j]。
同理如果state[i][j] == 3,表明Xi != Yj,length[i - 1][j] < length[i][j - 1],那么继续搜素state[i][j - 1]。