HLG 1478 最长公共子序列的最小字典序【深搜+ 最长公共子序列】
题意: 给定两个字符串,输出他们的最长公共子序列,要求字典序最小。
分析:
先用LCS算法将最长公共子序列的长度len求出来,还有数组f[i][j],表示字符串a前i个字符和字符串b前j个字符的最长公共子序列的长度,时间复杂度O(n * m)。
然后将两个字符串逆序,方便求最小字典序。
用两个二维数组分别记录两个字符串a和b前i个字符中字符c出现的最后位置,
la[i][c],字符串a的前i个字符中,字符c出现的最后位置。
la[i][c],字符串b的前i个字符中,字符c出现的最后位置。
然后从最大长度len到1枚举答案的每个位置的字符,直接暴力会超时,需要优化。
利用前面的f[][],la[],lb[]数组进行优化。
定义函数bool find(int k, int l1, int l2) 表示在字符串a的前l1个字符和字符串的前l2个字符中枚举答案的第k个字符,从最小字符开始枚举。假设当前枚举的字符是c,则第k个位置是字符c,那么这个字符在a和b出现的最后位置分别是t1=lsta[l1][c]和t2=lstb[l2][c],那么此时就应该有k == f[t1][t2],如果这个等式成立,则继续 find(k-1, t1- 1, t2 - 1),直到找完答案的第一个字符。
最后将答案逆序输出,就是最长公共子序列的最小字典序答案。
code:
#include<stdio.h> #include<string.h> #define clr(x)memset(x,0,sizeof(x)) char s1[1002],s2[1002]; int f[1001][1001]; // 表示字符串 a 前 i 个字符和字符串 b 前j个字符的最长公共子序列的长度。
int la[1002][27]; // la[i][c] 表示字符串 a 的前 i 个字符中,字符 c 出现的最后位置 int lb[1002][27]; // lb[i][c] 表示字符串 b 的前 i 个字符中,字符 c 出现的最后位置 int res[1002]; // 结果数组 int top,len; bool flag; int l_s() // 求出最长公共子序列的长度,顺便求出 f[][] 数组 { int c1[1002]={0},c2[1002]={0}; int l1=strlen(s1); int l2=strlen(s2); int i,j; for(i=0;i<l1;i++) { for(j=0;j<l2;j++) if(s1[i]==s2[j]) c1[j+1]=c2[j]+1; else c1[j+1]=c2[j+1]>c1[j]?c2[j+1]:c1[j]; for(j=1;j<=l2;j++) { c2[j]=c1[j]; f[i][j-1]=c2[j]; } } return c2[l2]; } void dfs(int k,int l1,int l2) // 深搜找符合条件的字典序最小的序列 { int i,t1,t2; if(flag) // 已经找到,不用再递归 return; if(k==0){ // 因为是按 字符顺序增大的 方向找的,所以第一次找到的 flag=true; // 即为最小字典序的序列 for(i=0;i<top;i++) printf("%c",res[i]+'a'); printf("\n"); return; } for(i=0;i<26;i++) { t1=la[l1][i]; // t1 为字母 i 在 a 串 中出现的最后一个位置 t2=lb[l2][i]; // 同上 if(f[t1][t2]==k) // 如果 a 串的前 t1 个字符和 b 串的前 t2 个字符 { // 的最长公共子序列 为 答案串 剩下的那部分长度,继续深搜 res[top++]=i; dfs(k-1,t1-1,t2-1); top--; } } } bool v[28]; int main() { //freopen("D:ce.txt","r",stdin); int i,j,l1,l2; char c; while(scanf("%s%s",&s1,&s2)!=EOF) { l1=strlen(s1); l2=strlen(s2); for(i=0;i<l1/2;i++){ // 字符串倒置 c=s1[i]; s1[i]=s1[l1-i-1]; s1[l1-i-1]=c; } for(i=0;i<l2/2;i++){ c=s2[i]; s2[i]=s2[l2-i-1]; s2[l2-i-1]=c; } len=l_s(); // 最长公共子序列的长度 memset(la,0xff,sizeof(la)); // 初始为 负值 memset(lb,0xff,sizeof(lb)); for(j=l1-1;j>=0;j--) // 预处理出 la[][]数组 { clr(v); for(i=j;i>=0;i--) if(!v[s1[i]-'a']) { v[s1[i]-'a']=true; la[j][s1[i]-'a']=i; } } for(j=l2-1;j>=0;j--) // 预处理出 lb[][]数组 { clr(v); for(i=j;i>=0;i--) if(!v[s2[i]-'a']) { v[s2[i]-'a']=true; lb[j][s2[i]-'a']=i; } } flag=false; top=0; dfs(len,l1-1,l2-1); } return 0; }