poj 1934 Trip 多个最长公共子序列

题目要你按字典序输出两个字符串的多个最长公共子序列

转自http://blog.csdn.net/tsaid/article/details/6726698

思路:

先用动态规划求两个字符串的最长公共子序列的保存在dp[i][j];dp[i][j]表示s1字符串1is2字符串1j的最长公共子序列的长度

然后用两个变量last1[i][j],last2[i][j]来分别保存字符j(a的序号为0b的序号为1.....z的序号为25)在字符串1-i中出现的最大标号,要是字符j没有出现,last[i][j]= 0;

然后从两个字符串的长度len1len2开始枚举a---z字符,比如开始 t1 = last1[len1][0], t2 = last2[len2][0]表示as1字符串1---len1的最大下标为t1, s2字符串1--len2的最大下标为t2,那么若dp[t1][t2] 的值为s1s2的最大公共子序列长度cnt则表示这个字符符合,保存起来,否则枚举下一个字符b。若满足情况的话,在继续在t1-1 t2 - 1 符合最大公共子序列为cnt - 1的字符串保存,如此循环,知道到达最长公共子序列为0时结束。把保存的字符串放入set集合里面,让它按字典序排序。


#include <iostream>
#include <cstdio>
#include <cstring>
#include <string>
#include <set>
using namespace std;

int const maxn = 100;
char s1[maxn], s2[maxn], tmp[maxn];
int dp[maxn][maxn], last1[100][30], last2[100][30], len1, len2, cnt;
set<string> collection;

void LCA();
void handle(char s[], int len, int last[][30]);
void find(int index1, int index2, int len);

int main()
{
   
    scanf("%s %s", &s1[1], &s2[1]);
    LCA();
    
    handle(s1, len1, last1);
    handle(s2, len2, last2);
    
    cnt = dp[len1][len2];
    tmp[cnt+1] = '\0';
    find(len1, len2, cnt);
    for(set<string>::iterator iter = collection.begin(); iter != collection.end(); iter++)
        printf("%s\n", iter->c_str());
    
    return 0;
}

void LCA()
{
    int i, j;
    for(i = 1; s1[i] != '\0'; i++)
    {
        for(j = 1; s2[j] != '\0'; j++)
        {
            if(s1[i] == s2[j])
                dp[i][j] = dp[i-1][j-1] + 1;
            else if(dp[i-1][j] >= dp[i][j-1])
                dp[i][j] = dp[i-1][j];
            else
                dp[i][j] = dp[i][j-1];
        }
    }
    len1 = i - 1;
    len2 = j - 1;
}

void handle(char s[], int len, int last[][30])
{
    for(int i = 0; i < 26; i++)
    {
        char c = 'a' + i;
        for(int j = 1; j <= len; j++)
        {
            int k;
            for(k = j; k >= 1; k--)
            {
                if(c == s[k])
                {
                    last[j][i] = k;
                    break;
                }
            }
            
            if(k == 0)
                last[j][i] = 0;
        }
    }
}

void find(int index1, int index2, int len)
{
    if(len <= 0)
    {
        collection.insert(&tmp[1]);
        return;
    }
    
    if(index1 > 0 && index2 > 0)
    {
        for(int i = 0; i < 26; i++)
        {
            int t1 = last1[index1][i];
            int t2 = last2[index2][i];
            if(dp[t1][t2] == len)
            {
                tmp[len] = 'a' + i;
                find(t1 - 1, t2 - 1, len - 1);
            }
        }
    }
}


  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值