UVA 11404 Plalidromic Subsquence

最长回文子序列可以用求解原串s和反转串rv的LCS来得到,因为要求回文串分奇偶,dp[i][j]保存长度,

要求字典序最小,dp[i][j]应该表示回文串的端点,所以边界为单个字符,即i+j=len+1。

这题最麻烦的地方在于字典序,我是写了个比较函数,有点暴力(常数大)。

也可以反着定义,这时结点就要保存那个状态的字符串了(这样定义比较字典序的时候常数小)

#include<bits/stdc++.h>
using namespace std;

#define MP make_pair
#define fi first
#define se second

const int LEN = 1e3+5;
char s[LEN],rv[LEN];
int dp[LEN][LEN];
pair<int,int> pre[LEN][LEN];
char val[LEN][LEN];

inline void updata(int i,int j,int v,char c,const pair<int,int> &prv)
{
    dp[i][j] = v;
    pre[i][j] = prv;
    val[i][j] = c;
}

const auto nil = MP(0,0);

#define dim(x) [x.fi][x.se]
bool cmpLex(pair<int,int> a,pair<int,int> b)
{
    //
    while(a != nil && val[a.fi][a.se] == val[b.fi][b.se]){
        a = pre dim(a); b = pre dim(b);
    }
    return val dim(a) < val dim(b);
}

//#define LOCAL
int main()
{
#ifdef LOCAL
    freopen("in.txt","r",stdin);
#endif

    while(gets(s)){
        int len = strlen(s);
        for(int i = 0; i < len; i++){
            rv[len-1-i] = s[i];
        }
        int hd1,hd2,vl = 0;
        for(int i = 1; i <= len; i++){
            int j = len+1-i;
            dp[i][j] = 1; val[i][j] = s[i-1]; pre[i][j] = nil;
            if(dp[i][j] > vl || ( dp[i][j] == vl && cmpLex( MP(i,j), MP(hd1,hd2) ) ) ){//
                vl = dp[i][j];
                hd1 = i; hd2 = j;
            }
            for(int k = 1; k < j; k++) dp[i][k] = 0;
            for(j++; j <= len; j++){
                if(s[i-1] == rv[j-1]){
                    updata(i,j,dp[i-1][j-1]+2,s[i-1],make_pair(i-1,j-1));
                    if(dp[i][j] > vl || (dp[i][j] == vl && cmpLex(MP(i,j),MP(hd1,hd2)) ) ){//
                        vl = dp[i][j];
                        hd1 = i; hd2 = j;
                    }
                }else {
                    if(dp[i-1][j] > dp[i][j-1] || (dp[i-1][j] == dp[i][j-1] && cmpLex(MP(i-1,j),MP(i,j-1)) ) ){//
                        updata(i,j,dp[i-1][j],val[i-1][j],pre[i-1][j]);
                    }else {
                        updata(i,j,dp[i][j-1],val[i][j-1],pre[i][j-1]);
                    }
                }

            }
        }
        int pv = (vl+1)>>1,ln = vl;

        auto u = MP(hd1,hd2);
        for(int i = 0; i < pv; i++){
            s[i] = val[u.fi][u.se];
            u = pre[u.fi][u.se];
        }

        s[ln] = ‘\0‘;
        for(int i = pv; i < ln; i++){
            s[i] = s[ln-1-i];
        }
        puts(s);
    }
    return 0;
}
时间: 2024-08-04 18:22:27

UVA 11404 Plalidromic Subsquence的相关文章

UVA 11404 Palindromic Subsequence[DP LCS 打印]

UVA - 11404 Palindromic Subsequence 题意:一个字符串,删去0个或多个字符,输出字典序最小且最长的回文字符串 不要求路径区间DP都可以做 然而要字典序最小 倒过来求LCS,转移同时维护f[i][j].s为当前状态字典序最小最优解 f[n][n].s的前半部分一定是回文串的前半部分(想想就行了) 当s的长度为奇时要多输出一个(因为这样长度+1,并且字典序保证最小(如axyzb  bzyxa,就是axb)) // // main.cpp // uva11404 //

UVA 11404 五 Palindromic Subsequence

Palindromic Subsequence Time Limit:3000MS     Memory Limit:0KB     64bit IO Format:%lld & %llu Submit Status Practice UVA 11404 1 #include <stdio.h> 2 #include <string.h> 3 #include <algorithm> 4 #include <string> 5 #include &l

uva 11404 dp

UVA 11404 - Palindromic Subsequence 求给定字符串的最长回文子序列,长度一样的输出字典序最小的. 对于 [l, r] 区间的最长回文串,他可能是[l+1, r] 和[l, r-1]两个区间的结果.或者当s[l] == s[r]时,区间[l+1, r-1]的结果再加上以s[l], s[r]为首尾的子序列. dp[l][r] = ans(dp[l][r-1], dp[l+1][r], s[l] + dp[l+1][r-1] + s[r]) dp存的是子序列,即一个s

LPS UVA 11404 Palindromic Subsequence

题目传送门 1 /* 2 LPS(Longest Palidromic Subsequence):最长回文子序列,和回文串不同,子序列是可以不连续的. 3 转化为LCS问题,将字符串逆序,然后和本串求LCS就是LPS的长度(为啥不就是LPS?),但是前一半是LPS的一半,可以补全 4 推荐->还有一种写法是用了LCS的思想,dp[i][j]表示i到j的最长回文串长度,状态转移方程: 5 1. dp[j][j+i-1] = dp[j+1][j+i-2] + 2; (str[j] == str[j+

UVA 11404 Palindromic Subsequence

题解: 神一般的trick 首先求最长回文字符串的长度.只用反过来.转换为LCS问题就行 但是关键的要输出字典序最小的 所以在LCS的过程中.保存相应的字符串,并且保证字符串最小.这么求得的长度是对的.但是不一定是回文字符串 例如 bcbabccb bccbabcb ---> bcabc. 想了很久.可能是因为字典序的问题.当前面一半固定的时候,后面一半应该是前面一半的翻转过来,但是有可能出现和前面一半一样的情景.....(有毒) 具体修改.就是取前面一半即可 代码: #include<bit

UVa 11404 回文子序列(LCS求最长回文串长度)

https://vjudge.net/problem/UVA-11404 题意: 给定一个由小写字母组成的字符串,删除其中的0个或多个字符,使得剩下的字母(顺序不变)组成一个尽量长的回文串.如果有多解,输出字典序最小的解. 思路: 首先,最长回文子串的长度可以通过正序字符串和逆序字符串进行LCS得出. 但是这道题目麻烦的是还要输出这个回文串,并且字典序得最小. 应用的主要还是LCS的思想方法,不过在进行状态转移的时候,再加上字符串的状态转移. 不过最后得到的字符串不一定是回文串,但是它的前一半肯

dp题目列表

10271 - Chopsticks 10739 - String to Palindrome 10453 - Make Palindrome 10401 - Injured Queen Problem 825 - Walking on the Safe Side 10617 - Again Palindrome 10201 - Adventures in Moving - Part IV 11258 - String Partition 10564 - Paths through the Ho

训练指南DP阶段训练1

最近又忙又颓.............时间抓不紧....下学期开始就要准备考研了.......就2个月左右可以做自己喜欢的事了....争取把紫书和白书没做的,做过的..来一次完整的总结 训练指南上面的5个例题+后面15个习题是第一阶段 vjudge训练地址 http://vjudge.net/contest/139533#overview -------------------------------------------------------------------------------

UVA 562 Dividing coins --01背包的变形

01背包的变形. 先算出硬币面值的总和,然后此题变成求背包容量为V=sum/2时,能装的最多的硬币,然后将剩余的面值和它相减取一个绝对值就是最小的差值. 代码: #include <iostream> #include <cstdio> #include <cstring> #include <cmath> #include <algorithm> using namespace std; #define N 50007 int c[102],d