UVa 11404 回文子序列 LPS

题目:http://acm.hust.edu.cn/vjudge/contest/129795#problem/L
题意:

给定一个由小写字母组成的字符串,删除其中的0个或者多个字符,使得剩下的字母(顺序不变)组成一个尽量长的回文串,如果有多解,输出字典序最小的解

思路:
很容易想到的一个做法就是将字符串翻转,然后求正串和反串的最长公共子序列,因为题目要求输出这个序列(字典序最小),用stirng保存一下即可。
这篇博客有详解:http://blog.youkuaiyun.com/shuangde800/article/details/9898675

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<iostream>
using namespace std;
typedef long long ll;
const int INF = 1e9 + 9;
const int N = 1e3+ 9;
char a[N],b[N];

struct Item {
    int len;
    string str;
};
Item d[N][N];
int main() {
    freopen ("f.txt","r",stdin);
    int n,T,t,x;
    int cas=0;
    while (~scanf ("%s",&a) ) {

        int n=strlen (a);
        for (int i=0; i<n; i++) b[i]=a[n-i-1];

        for (int i=0; i<=n; i++)
            for (int j=0; j<=n; j++)
                d[i][j].len=0,d[i][j].str="";
        for (int i=0; i<n; i++) {
            for (int j=0; j<n; j++)
                if (a[i]==b[j]) {
                    d[i+1][j+1].len=d[i][j].len+1;
                    d[i+1][j+1].str=d[i][j].str+a[i];
                } else {
                    if (d[i+1][j].len>d[i][j+1].len) d[i+1][j+1]=d[i+1][j];
                    else if (d[i+1][j].len<d[i][j+1].len) d[i+1][j+1]=d[i][j+1];
                    else if (d[i+1][j].str<d[i][j+1].str) d[i+1][j+1]=d[i+1][j];
                    else d[i+1][j+1]=d[i][j+1];
                }
        }
        int maxn=d[n][n].len;
        string ans=d[n][n].str;
        if (maxn&1) {
            for (int i=0; i<maxn/2; i++) printf ("%c",ans[i]);
            for (int i=maxn/2; i>=0; i--) printf ("%c",ans[i]);
        } else {
            for (int i=0; i<maxn/2; i++) printf ("%c",ans[i]);
            for (int i=maxn/2-1; i>=0; i--) printf ("%c",ans[i]);
        }
        printf ("\n");
    }
    return 0;
}

这题其实是LPS(Longest Palindromic Subsequence)问题,类似于LCS问题也可以设计个相似的dp状态:
dp[i][j]表示子串s[i…j]之间可以构成的LPS个数,
那么dp转移方程就是:

if(s[i]==s[j])d[i][j]=d[i+1][j-1]+2;
else d[i][j]=max(d[i+1][j],d[i][j-1]);

转移的时候如果直接枚举i,j,肯定会超时。所以划分区间,区间从小到大转移一下就好。

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<iostream>
using namespace std;
typedef long long ll;
const int INF = 1e9 + 9;

const int N = 1e3+ 9;
char s[N];
string ans[N][N];
int d[N][N];
void LPS()
{
    int n=strlen(s+1);
    memset(d,0,sizeof(d));
    for(int i=1;i<=n;i++)d[i][i]=1;
    for(int i=1;i<=n;i++)ans[i][i]=s[i];
    for(int L=2;L<=n;L++){
        for(int i=1;i+L-1<=n;i++){
            int j=i+L-1;
            if(s[i]==s[j]){
                if(L==2){
                    d[i][j]=2;
                    ans[i][j]=ans[i][i]+ans[j][j];
                    continue;
                }
                d[i][j]=d[i+1][j-1]+2;
                ans[i][j]=s[i]+ans[i+1][j-1]+s[j];
            }
            else if(d[i+1][j]>d[i][j-1]){
                d[i][j]=d[i+1][j];
                ans[i][j]=ans[i+1][j];
            }
            else if(d[i+1][j]<d[i][j-1]){
                d[i][j]=d[i][j-1];
                ans[i][j]=ans[i][j-1];
            }
            else{
                d[i][j]=d[i+1][j];
                ans[i][j]=min(ans[i+1][j],ans[i][j-1]);
            }
        }
    }
    cout<<ans[1][n]<<endl;
}
int main() {
    //freopen ("f.txt","r",stdin);

    while (~scanf ("%s",s+1) ) {
        LPS();
    }
    return 0;
}
/*
SampleInput
aabbaabb
computer
abzla
samhita

SampleOutput
aabbaa
c
aba
aha

*/
最长回文子序列(Longest Palindromic Subsequence,LPS)问题是指在一个给定的字符串中找到一个最长的回文子序列回文子序列是指一个序列本身不是回文串,但它是一个回文串的子序列。 在C++中,我们可以使用动态规划(Dynamic Programming,DP)的方法来解决这个问题。动态规划的主要思想是将一个大问题分解成小问题,然后从小问题出发,逐渐求得大问题的解。 以下是一个使用动态规划解决最长回文子序列问题的C++示例代码: ```cpp #include <iostream> #include <vector> #include <string> using namespace std; // 函数用于计算字符串str的最长回文子序列的长度 int longestPalindromeSubseq(string str) { int n = str.size(); // 创建一个二维数组dp,用于存储子问题的解,初始化所有值为0 vector<vector<int>> dp(n, vector<int>(n, 0)); // 单个字符的最长回文子序列长度为1,所以对角线上的元素设置为1 for (int i = 0; i < n; i++) { dp[i][i] = 1; } // 如果两个字符相同,那么它俩组成的子序列长度为2 for (int cl = 2; cl <= n; cl++) { for (int i = 0; i < n - cl + 1; i++) { int j = i + cl - 1; if (str[i] == str[j] && cl == 2) { dp[i][j] = 2; } else if (str[i] == str[j]) { dp[i][j] = dp[i + 1][j - 1] + 2; } else { dp[i][j] = max(dp[i][j - 1], dp[i + 1][j]); } } } // 返回整个字符串的最长回文子序列长度 return dp[0][n - 1]; } int main() { string str; cout << "请输入一个字符串:" << endl; cin >> str; cout << "最长回文子序列的长度为:" << longestPalindromeSubseq(str) << endl; return 0; } ``` 在这段代码中,`dp[i][j]`表示从字符串的第`i`个字符到第`j`个字符组成的子串的最长回文子序列的长度。通过初始化对角线以及递推式逐步填充这个二维数组,最终可以得到整个字符串的最长回文子序列长度。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值