题目:求两个串的最长公共递增子串
设f[i,j]表示以A串的前i个,B串的前j个,而且以B[j]结尾的最长的公共子串的长度。则有:
f[i, j] = f[i-1, j] 当B[j] A[i]时
f[i, j] = max{f[i-1, k]+1 其中1 && B[j]>B[k]} 当B[j] == A[i]时
最终结果就是最大f.
算法复杂度:O(n^3)
上面求max的那一步可以优化,使最终复杂度为O(n^2): DP进行计算时,外层循环为A[i],内层的循环为B[j].求max只发生在B[j]=A[i],此时往前寻找满足B[k]<B[j]的数,所以B[k]<B[j]=A[i].因此在进入内层循环时,就可以维护一个最大值m,当B[k]<A[i]时m=max{m, f[i-1, k]}.然后当出现B[j]=A[i]时,就可以直接取f[i, j]=m+1.
代码:
#include <iostream>
#include <stack>
#include <cstring>
using namespace std;
const int MAX_N = 500;
int na, nb; // 两个串分别的实际长度
int sa[MAX_N+1]; // 第一个串
int sb[MAX_N+1]; // 第二个串
int f[MAX_N+1][MAX_N+1];
int pre[MAX_N+1][MAX_N+1]; // 保存前一个相等的位置
void compute() {
for(int i = 1; i <= na; i++) {
int m = 0; // 目前所看到的最大值
int index = 0; // 最大值的下标
for(int j = 1; j <= nb; j++) {
if(sa[i] == sb[j]) {
f[i][j] = m + 1;
pre[i][j] = index;
} else {
f[i][j] = f[i-1][j];
}
if(sb[j] < sa[i] && f[i-1][j] > m) { // 更新最大值和下标
m = f[i-1][j];
index = j;
}
}
}
// 求最大值
int longest = 0;
for(int j = 1; j <= nb; j++) {
if(f[na][j] > f[na][longest]) longest = j;
}
// 根据pre求序列
stack<int> st;
int n = f[na][longest];
cout << n << endl;
int indexi = na;
int indexj = longest;
while(n--) {
st.push(sb[indexj]);
while(sa[indexi] != sb[indexj]) indexi--;
indexj = pre[indexi][indexj];
indexi--;
}
while(!st.empty()) {
cout << st.top() << " ";
st.pop();
}
cout << endl;
}
int main() {
memset(f, 0, sizeof(f));
memset(pre, 0, sizeof(pre));
cin >> na;
for(int i = 1; i <= na; i++) cin >> sa[i];
cin >> nb;
for(int i = 1; i <= nb; i++) cin >> sb[i];
compute();
return 0;
}