2035-It Prefokery Pio
問題概要
文字列Aの部分列のうち最長の回文となるものを求めよ.
解法
文字列とその反転文字列に対しLCSのアルゴリズムを適用する.
最長共通部分列が回文ならば明らかに,それは最長回文になり,そして最長共通部分列は必ず回文になる.
以下簡単な証明
対象文字列をA,対象文字列を反転したものをRev(A)とする.
AとRev(A)の部分列をTとすると,Rev(A)とRev(Rev(A))=Aの部分列にRev(T)は含まれる.
よって,Tが最長共通部分列の時に回文では無かったと過程すると,それより長い部分列が考えつくので矛盾する.
ゆえにTが最長共通部分列の時は回文となる.
実装(C++)
#include <algorithm> #include <vector> #include <iostream> #include <cstring> using namespace std; typedef long long int lli; #define REP(i,x) for(int i=0;i<(int)(x);i++) #define rep(i,s,x) for(int i=s;i<(int)(x);i++) #define FOR(i,c) for(__typeof((c).begin())i=(c).begin();i!=(c).end();i++) #define RREP(i,x) for(int i=(x);i>=0;i--) #define RFOR(i,c) for(__typeof((c).rbegin())i=(c).rbegin();i!=(c).rend();i++) string a,b; vector<int> lcs(const vector<int>& a, const vector<int>& b) { const int n = a.size(), m = b.size(); vector< vector<int> > X(n+1, vector<int>(m+1)); vector< vector<int> > Y(n+1, vector<int>(m+1)); for (int i = 0; i < n; ++i) { for (int j = 0; j < m; ++j) { if (a[i] == b[j]) { X[i+1][j+1] = X[i][j] + 1; Y[i+1][j+1] = 0; } else if (X[i+1][j] < X[i][j+1]) { X[i+1][j+1] = X[i][j+1]; Y[i+1][j+1] = +1; } else { X[i+1][j+1] = X[i+1][j]; Y[i+1][j+1] = -1; } } } vector<int> c; for (int i = n, j = m; i > 0 && j > 0; ) { if (Y[i][j] > 0) --i; else if (Y[i][j] < 0) --j; else { c.push_back(a[i-1]); --i; --j; } } reverse(c.begin(), c.end()); return c; } int main(){ for(;cin>>a;){ a; int n=a.size(); vector<int> k1,k2; REP(i,n)k1.push_back(a[i]); REP(i,n)k2.push_back(a[n-i-1]); vector<int> t=lcs(k1,k2); REP(i,t.size()){ cout<<(char)t[i]; } cout<<endl; } }