问题:两个字符串求其最长可不连续相同子串,打印出子串
思考:这样的问题可以用回溯的方法,通过递归把每种情况都遍历一遍,再得出最佳结果,但是这样的解法的时间发杂度为2的n次方,当字符串长度达到10以上时首先空间会溢出,再者时间消耗太多。所以考虑用记忆化搜索,用二维数组存储两个字符串中没两个元素之间的关系。
在用二维数组存储关系的难题在于如何去遍历,让每个节点的值是到达它的最大值,从子串的定义可以看出,当遍历到的节点为真时,就需要从它的下一列和下一行开始前进,所以我们需要遍历完它的右下方的所有节点。在这种情况下的时间复杂度为n的4次方。
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <stack>
using namespace std;
int mtx[1001][1001];
int fa[1001];//每个节点的双亲节点
int main(){
char s1[1001], s2[1001];
int l1, l2;
int T;
scanf("%d", &T);
while(T--){
memset(fa, -1, sizeof(fa));
scanf("%s%s", s1, s2);
l1 = strlen(s1);
l2 = strlen(s2);
for(int i = 0; i < l1; i++)
for(int j = 0; j < l2; j++) mtx[i][j] = (s1[i] == s2[j]);//记录两个字符串元素之间关系
for(int i = 0; i < l1; i++)
for(int j = 0; j < l2; j++) //从每个起点出发,且每个起点都在遍历过的起点的右下方
if(mtx[i][j])
for(int r = i+1; r < l1; r++)
for(int s = j+1; s < l2; s++)
if(mtx[r][s] && mtx[r][s] < mtx[i][j]+1) { //当该点元素不为零时,给它附上最大值
mtx[r][s] = mtx[i][j] + 1;
fa[r] = i;
}
int ma = 0, si = 0, sj = 0; //找到最大终点
for(int i = 0; i < l1; i++)
for(int j = 0; j < l2; j++)
if(mtx[i][j] > ma) { si = i; sj = j; }
stack<int> st;//以下是正序输出
while(si != -1){
st.push(si);
si = fa[si];
}
while(!st.empty()){
printf("%c", s1[st.top()]);
st.pop();
}
printf("\n");
}
}
for(int j = 0; j < l2; j++) //从每个起点出发,且每个起点都在遍历过的起点的右下方
if(mtx[i][j])
for(int r = i+1; r < l1; r++)
for(int s = j+1; s < l2; s++)
if(mtx[r][s] && mtx[r][s] < mtx[i][j]+1) { //当该点元素不为零时,给它附上最大值
mtx[r][s] = mtx[i][j] + 1;
fa[r] = i;
}
int ma = 0, si = 0, sj = 0; //找到最大终点
for(int i = 0; i < l1; i++)
for(int j = 0; j < l2; j++)
if(mtx[i][j] > ma) { si = i; sj = j; }
stack<int> st;//以下是正序输出
while(si != -1){
st.push(si);
si = fa[si];
}
while(!st.empty()){
printf("%c", s1[st.top()]);
st.pop();
}
printf("\n");
}
}