作为特别特别经典的一类序列DP,相信大家都应该了解LCS吧!
那么最基本的n方做法就只简述一下:
设f[i][j]表示第一个序列匹配到了第i位,第二个匹配到了第j位;
转移
f[i][j]=max (f[i-1][j], f[i][j-1]); if (a[i]==b[j]) f[i][j]=max (f[i][j], f[i-1][j-1]+1);
然后 n方此处不够优秀
对于两个都是1-n的一个排列的求LCS的问题(LuoGu P1439 模板)是可以nlogn解决的
考虑怎么优化 。
实际上我们可以发现,如果存在一组公共子序列,那么这就意味着他们在原序列中,对应位置的 相对大小 也是一定的(因为都是1-n)。
所以我们把其中一个序列的每一个值对应上其位置,另一个序列对应的转换,这样就只要求LIS就行了
例如 3 2 1 4 5 和 1 2 3 4 5:
把第一个先对应上其位置(用a数组),
a[3]=1, a[2]=2, a[1]=3, a[4]=4, a[5]=5
另外一个再对应一下,则得到3 2 1 4 5
这样再把1 2 3 4 5 和 3 2 1 4 5 求LIS 即 2 4 5,对应到原序列分别为 2 4 5就是他们的LCS。
这样问题就解决了。
Update:
题意:LCS方案输出。
思路:求出LCS长度后,预处理好前i个子母中,字母j出现的最后位置。
然后DFS,依次枚举每一位填什么字母,注意好条件的限制就可以了。
void dfs (int x,int y,int num,string ss) { if (num==0) {ans.insert(ss);return;} for (RG int i=1;i<=26;++i) { if (la[x][i]>=num&&lb[y][i]>=num&&f[la[x][i]][lb[y][i]]==num) dfs(la[x][i]-1,lb[y][i]-1,num-1,(char)(i+'a'-1)+ss); } }