C++
 Computer >> コンピューター >  >> プログラミング >> C++

C++で最長回文部分列の長さを求めるアルゴリズム


文字列 s が与えられたとき、その中に含まれる最長の回文部分列(パリンドローム・サブシーケンス)の長さを求める問題を考えます。文字列の最大長は 1000 と仮定できます。たとえば入力が "bbbab" の場合、出力は 4 になります。このとき "bbbb" が回文部分列の一例です。

解法のアプローチ

この問題は、「元の文字列 s と、それを逆順にした文字列 x の最長共通部分列(LCS)の長さを求める」ことで解決できます。回文は前後どちらから読んでも同じになるため、元の文字列と逆順の文字列の両方に共通して現れる最長の部分列こそが、最長回文部分列となるからです。

アルゴリズムの手順

  • x := s として x を逆順にし、n := s のサイズとする
  • n が 0 の場合は 0 を返す
  • s と x の先頭にそれぞれ空白を 1 つ追加する(インデックスを 1 始まりに揃えるため)
  • (n + 1) × (n + 1) のサイズの DP テーブル dp を作成する
  • i を 1 から n まで、j を 1 から n まで繰り返す:
    • dp[i][j] := max(dp[i][j − 1], dp[i − 1][j])
    • x[i] == s[j] の場合、dp[i][j] := max(dp[i][j], 1 + dp[i − 1][j − 1])
  • dp[n][n] を返す

それでは、理解を深めるために以下の実装例を見てみましょう。

実装例(C++)

#include <bits/stdc++.h>
using namespace std;
class Solution {
   public:
   int longestPalindromeSubseq(string s) {
      string x = s;
      reverse(x.begin(), x.end());
      int n = s.size();
      if(!n) return 0;
      s = " " + s;
      x = " " + x;
      int ret = 0;
      vector < vector <int> > dp(n + 1, vector <int>(n + 1));
      for(int i = 1; i <= n; i++){
         for(int j = 1; j <= n ; j++){
            dp[i][j] = max(dp[i][j - 1], dp[i - 1][j]);
            if(x[i] == s[j]) {
               dp[i][j] = max(dp[i][j], 1 + dp[i - 1][j - 1]);
            }
         }
      }
      return dp[n][n];
   }
};
main(){
   Solution ob;
   cout << (ob.longestPalindromeSubseq("bbbab"));
}

入力

"bbbab"

出力

4

計算量について

このアルゴリズムでは、DP テーブルの全マスを一度ずつ埋めていくため、時間計算量は O(n²)、空間計算量も同様に O(n²) となります。文字列の最大長が 1000 であっても、十分に現実的な時間で処理できる設計です。


  1. C++で最長増加部分列の個数を求める方法

    問題概要ソートされていない整数の配列が与えられたとき、「最長増加部分列(LIS: Longest Increasing Subsequence)」の個数を求める問題を考えます。例えば、入力が [1, 3, 5, 4, 7] の場合を考えてみましょう。このとき最長増加部分列は [1, 3, 5, 7] と [1, 3, 4, 7] の2通りが存在するため、出力は 2 となります。解法のアプローチこの問題は動的計画法(DP)を用いて効率的に解くことができます。ポイントは、各インデックスについて「その要素を末尾とする最長増加部分列の長さ」と「その長さとなる部分列の個数」の2つを同時に管理することです

  2. 最長共通部分列(LCS)を求めるC++プログラム

    部分列とは、元の文字列から要素を取り出す際に、元の順序を保ったまま作られる列のことです。例えば、文字列「stuv」の部分列には「stu」「tuv」「suv」などがあります。長さnの文字列から作成できる部分列の数は、2n通り存在します。そのため、すべての部分列を総当たりで調べる方法は、文字列が長くなるほど計算量が爆発的に増えてしまいます。最長共通部分列(LCS)とは最長共通部分列(Longest Common Subsequence:LCS)とは、2つの文字列に共通して現れる部分列の中で、最も長いものを指します。例えば、文字列「ABCDGH」と「AEDFHR」の場合、最長共通部分列は「ADH」と