C++で最長の繰り返し部分文字列を求める方法
文字列Sが与えられたとき、その中に含まれる最も長く繰り返される部分文字列の長さを求めることを考えます。繰り返される部分文字列が存在しない場合は0を返します。
例えば、文字列が「abbaba」の場合、出力は2になります。これは、最も長い繰り返し部分文字列が「ab」または「ba」(長さ2)であるためです。
解法のアプローチ:動的計画法(DP)
この問題は、動的計画法を用いて効率的に解くことができます。dp[i][j]を「位置iで終わる部分文字列と位置jで終わる部分文字列の共通接尾辞の長さ」と定義します。文字が一致するたびに、直前の状態に1を加えていくことで、繰り返し部分文字列の長さを段階的に求められます。
具体的には、以下の手順に従います。
- n := 文字列Sのサイズとする
- S := 先頭に空白1文字を連結した文字列とする(インデックスを1始まりにするため)
- ret := 0 で初期化する
- サイズ (n + 1) × (n + 1) の二次元配列 dp を作成する
- i を 1 から n までループする
- j を i + 1 から n までループする
- S[i] == S[j] の場合:
- dp[i][j] := max(dp[i][j], 1 + dp[i - 1][j - 1])
- ret := max(ret, dp[i][j])
- S[i] == S[j] の場合:
- j を i + 1 から n までループする
- ret を返す
それでは、以下の実装例を見て理解を深めましょう。
C++での実装例
#include <bits/stdc++.h>
using namespace std;
class Solution {
public:
int longestRepeatingSubstring(string S) {
int n = S.size();
S = " " + S;
int ret = 0;
vector < vector <int> > dp(n + 1, vector <int> (n + 1));
for(int i = 1; i <= n; i++){
for(int j = i + 1; j <= n; j++){
if(S[i] == S[j]){
dp[i][j] = max(dp[i][j], 1 + dp[i - 1][j - 1]);
ret = max(ret, dp[i][j]);
}
}
}
return ret;
}
};
main(){
Solution ob;
cout << (ob.longestRepeatingSubstring("abbaba"));
}入力
"abbaba"
出力
2
計算量について
このアルゴリズムの時間計算量はO(n²)、空間計算量もO(n²)です。文字列の長さがnの場合、二重ループですべての文字ペア(i, j)を確認し、各ペアに対して定数時間の処理を行うためです。二次元DPテーブルを使用することで、部分問題の結果を再利用し、重複計算を避けている点がポイントです。
-
【C++入門】substr()関数で部分文字列を取得する方法
C++における部分文字列とは 部分文字列(substring)とは、ある文字列の一部分を指します。C++では、標準ライブラリのsubstr()関数を使うことで、元の文字列から任意の部分文字列を簡単に取り出せます。 substr()関数は、次の2つの引数を受け取ります。 pos:部分文字列の抽出を開始する位置(先頭の文字は0番目) len:抽出する文字数 以下に、C++で部分文字列を取得するプログラムの例を示します。 サンプルコード #include <iostream> #include <string.h> using namespace std; int ma
-
Pythonで重複文字を含まない最長部分文字列を求めるアルゴリズム
文字列が与えられたとき、同じ文字が繰り返されない最長の部分文字列(連続した部分)の長さを求める問題です。例えば、文字列が「ABCABCBB」である場合、答えは 3 になります。これは、重複する文字を一切含まない部分文字列として「ABC」(長さ3)が存在するためです。解法のアプローチこの問題は、スライディングウィンドウ(尺取り法)とハッシュマップを組み合わせることで効率的に解くことができます。各文字が最後に出現した位置を記録しながら、ウィンドウの左端を適切に移動させるのがポイントです。アルゴリズムの手順i := 0、j := 0 と初期化し、文字の出現位置を記録するためのマップ(辞書)を用意しま