DP 最长公共子序列并标记输出

来源:互联网 发布:linux卸载光盘 编辑:程序博客网 时间:2024/06/01 07:26
/**    动规是必考题目,有时甚至考好几道,非常灵活,非常巧妙    最长公共子序列是动规的经典题目,也被用于好多教材的例子    谈到动态规划,我对其也是最肤浅的认识,感觉和记忆化搜索    是不分家的,我一般都是先把整个问题局部化,然后分析局部    大致写个 转移方程 再验证是否正确并考虑特殊情况,不管怎    么样,只要经过大量重复练习,神马都是浮云    求其最长的子序列,如:    abcdef    bdaf    这两个串的最长子序列为 bdf    子序列和子串不一样,子串必须连续,子序列不需要连续,它    只抢到字母的先后顺序,如果求最长子串,这就难了,当然可    以被后缀数组秒杀,后面会贴    只要提到两个串,然后又和DP 挂钩,大多都是二维数组f[M][M],    M为串的最大长度,f[i][j] 表示a串前 i的字符和b 串前 j 的字    符所得到的最优解,逐个递推到f[M][N];    这个并不好理解,只有自己一遍遍手动模拟,然后才会有感觉,    char s1[M], s2[M];    scanf("%s%s", s1+1, s2+1);    //这样输入可以使下面处理简单点,即s1[1] 为串的第一个字符,而不是s1[0]    int f[M][M]; //用来保存子问题和问题的结果    //f 数组初始化为0    //设l1, l2 为s1, s2串长度    for (int i=1; i<=l1; i++)        for (int j=1; j<=l2; j++) {  //之所以从1 开始,因为下面有 f[i-1][j-1]            if (s1[i] == s2[j])                f[i][j] = f[i-1][j-1] + 1; //这是显然的            else                f[i][j] = max(f[i-1][j], f[i][j-1]); //如果不相等就继承一个最大的        }    上面虽然看起来简单,但是对于刚接触的编程的大学生理解起来也是很费劲的    如果用手也模拟不出来,最好还是本算法书看下具体意思了,相信几乎每本上面    都有这个例子    上面f[l1][l2] 给出了最常的公共子序列长度,可是如果想具体打印出来怎么办    当然得标记下    int path[M][M]; //这是一个标记数组,总共有3 种情况,所以有3 种取值    //初始化为0    for (int i=1; i<=l1; i++)        for (int j=1; j<=l2; j++) {            if (s1[i] == s2[j]) {                path[i][j] = 1;     //这种情况标记为1                f[i][j] = f[i-1][j-1] + 1;            }            else if (f[i-1][j] > f[i][j-1]) {                path[i][j] = 2;                f[i][j] = f[i-1][j];            }            else {                path[i][j] = 3;                f[i][j] = f[i][j-1];            }        }    //现在path 已经标记了所有的步骤,现在来递归输出    void print(int i, int j) {        if (path[i][j] == 0)            return;        if (path[i][j] == 1) {            print(i-1, j-1);            printf("%c ", s1[i]); //当然也可以是 s2[j]        }        else if (path[i][j] == 2)            print(i-1, j);        else            print(i, j-1);    }    调用 print(l1, l2); 即可输出    POJ 2250    睡觉了,睡觉了,晚安*/收藏于 2012-01-15来自于百度空间

0 0
原创粉丝点击