最长回文子序列可以用求解原串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-10-17 00:00:47