回文検出(DP)
回文検出は、以下の記事で書いたManacherのアルゴリズムを使えばで解けるが、かければDPでも解くことが出来る。
DPの方が遅いが、コード的にはかなりシンプルに書けるのでメモしておく。
klee.hatenablog.jp
個人的にはManacherは偶数長の時とか、探索範囲の記述でバグを埋めそうで怖い。(ライブラリ化してるからよっぽど問題はないが)
どう解くか
以下のような2次元配列でDP配列の状態を定義する。
このDPテーブルを以下のルールに従って埋めていく。
の時
必ずなので
の時
の時、
の時
まずである必要がある。ただし、この条件だけでは不十分で、これに加えて一個内側の部分文字列が回文であれば良い。
これはとのをそれぞれ考えればわかるが、では明らかに内側であるが回文ではないのでも回文にならないが、は問題ない。
遷移の計算時、の更新のためにはが必要になるので、は逆順で探索する必要があることに注意する。
実装
#include <algorithm> #include <cmath> #include <cstring> #include <deque> #include <iomanip> #include <iostream> #include <map> #include <queue> #include <set> #include <stack> #include <string> #include <vector> #define pb push_back #define rep(i, n) for (int i = 0; i < (n); i++) #define reps(i, n, s) for (int i = (s); i < (n); i++) #define rrep(i, n) for (int i = (n - 1); i >= 0; i--) #define rreps(i, n, s) for (int i = s; i >= n; i--) using ll = long long; using namespace std; constexpr long long MAX = 5100000; constexpr long long INF = 1LL << 60; constexpr int MOD = 1000000007; int main() { cin.tie(0); ios::sync_with_stdio(false); string s; cin >> s; int n = s.size(); vector<vector<bool>> dp(n, vector<bool>(n, false)); int max_len = 0; int l, r; rrep(i, n) {//逆順 reps(j, n, i) { if (j - i <= 1) { dp[i][j] = (s[i] == s[j]); } else { dp[i][j] = dp[i + 1][j - 1] && (s[i] == s[j]); } if (dp[i][j] && max_len < j - i + 1) { max_len = j - i + 1; l = i; r = j; } } } string res = s.substr(l, r - l + 1); cout << res << endl; return 0; }
簡単で安心。
計算量が許せばこっちを書きたい。