C++で文字列Sに一致する部分列(サブシーケンス)の数を求める方法
文字列 S と単語のリスト words が与えられたとき、S の部分列(サブシーケンス)となっている words[i] の個数を求める問題です。たとえば、入力が S = "abcde"、辞書が ["a", "bb", "acd", "ace"] の場合、出力は 3 になります。これは、辞書の中に「a」「acd」「ace」の3つの単語が S の部分列として含まれているためです("bb" は 'b' が連続して2つ必要ですが、S 内に 'b' は1つしかないため一致しません)。
解法のアプローチ
この問題は、各単語を「次に照合すべき文字」をキーとしてマップにグループ化し、文字列 S を先頭から1文字ずつ走査しながら待機中の単語を前進させていく手法で効率的に解けます。具体的な手順は以下の通りです。
- 変数
nに words 配列のサイズを代入します。 - マップ
mを作成します(キー:文字、値:文字列のベクトル)。 - i を 0 から words のサイズまでループし、各単語
words[i]をm[words[i][0]](先頭文字をキーとするエントリ)に挿入します。 - 答えを保持する
ansを 0 で初期化します。 - i を 0 から S のサイズまでループします。
- 文字
xにS[i]を代入します。 xがマップmに存在する場合は、次の処理を行います。tempにm[x]を取り出し、m[x]を削除します。- j を 0 から temp のサイズまでループします。
temp[j]のサイズが 1 ならansを 1 増やします(その単語は完全に一致したことを意味します)。そうでなければ、temp[j]の先頭1文字を除いた部分文字列をm[temp[j][1]]に挿入します。
- 文字
- 最後に
ansを返します。
C++による実装例
それでは、理解を深めるために実際の実装を見てみましょう。
#include <bits/stdc++.h>
using namespace std;
class Solution {
public:
int numMatchingSubseq(string S, vector<string>& words) {
int n = words.size();
map <char, vector <string> > m;
for(int i = 0; i < words.size(); i++){
m[words[i][0]].push_back(words[i]);
}
int ans = 0;
for(int i = 0; i < S.size(); i++){
char x = S[i];
if(m.find(x) != m.end()){
vector <string> temp = m[x];
m.erase(x);
for(int j = 0; j < temp.size(); j++){
if(temp[j].size() == 1){
ans++;
} else {
m[temp[j][1]].push_back(temp[j].substr(1));
}
}
}
}
return ans;
}
};
int main() {
Solution ob1;
string s = "abcde";
vector<string> v{"a","bb","acd","ace"};
cout << ob1.numMatchingSubseq(s, v) << endl;
return 0;
}
入力
"abcde"
["a","bb","acd","ace"]
string s = "abcde";
vector<string> v{"a","bb","acd","ace"};
出力
3
計算量のポイント
このアルゴリズムでは、文字列 S を一度だけ走査し、各ステップで待機中の単語の「次に必要な文字」だけを更新していきます。そのため、全体の時間計算量は O(|S| + Σ|words[i]|) 程度に抑えられます。各単語ごとに S を最初から走査し直す素朴な方法(O(|S| × 単語数))と比べて大幅に効率的であり、特に S が長く単語数が多い場合に有効なアプローチです。
-
サイズ d の正十二角形を作れる組み合わせの数を求める C++ プログラム
問題概要 整数 d が与えられたとします。ここで、一辺の長さが 1 の正方形タイルと正三角形タイルが無限枚あるものと考えます。これらのタイルを組み合わせて、一辺の長さが d の正十二角形(12 辺形)を作るとき、その作り方が何通りあるかを求めるのがこの問題です。答えが非常に大きくなる場合は、998244353 で割った余りを返します。 アプローチ この問題は、二項係数を利用することで効率的に解くことができます。結論から言うと、求めるべき答えは C(2d−1, d−1)、すなわち「2d−1 個の中から d−1 個を選ぶ組み合わせの総数」です。 階乗を直接計算すると値が急激に大きくなりオーバー
-
C++で五胞体数(ペンタトープ数)を求める方法
五胞体数とは? 五胞体数(ペンタトープ数)は、パスカルの三角形の第5の対角線上に現れる数列として知られています。この数列を定義するには、パスカルの三角形に少なくとも5つの数が必要となるため、数列の最初の数はパスカルの三角形の第4行である 1 4 6 4 1 から始まります。 本チュートリアルでは、n番目の五胞体数を求める方法を解説します。まずは具体的な例を見てみましょう。 入力 : 1出力 : 1入力 : 4出力 : 35 以下の図から出力を確認できます。 この問題は数列に関するものなので、解法ではまず数列のパターンを見つけることから始めます。 解法のアプローチ このプログラムでは、数列の