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

【C++】最小ウィンドウ部分列(Minimum Window Subsequence)の求め方

2つの文字列 S と T が与えられたとき、「T が部分列(サブシーケンス)として含まれる」ような S の最短の部分文字列 W を求める問題を考えてみましょう。S の中に T のすべての文字をカバーできるウィンドウが存在しない場合は空文字列を返し、条件を満たすウィンドウが複数ある場合は、開始位置が最も左側にあるものを返します。

たとえば、入力が S = "abcdebdde"、T = "bde" の場合、出力は "bcde" になります。これは "bdde" よりも先に出現するためです。また、"deb" が答えにならないのは、ウィンドウ内で T の文字が必ず順序どおりに現れる必要があるからです。

アルゴリズムの考え方

この問題は、前方向へのスキャンと後方向への巻き戻しを組み合わせた2ポインタ法で効率よく解くことができます。具体的な手順は以下のとおりです。

  1. 変数を初期化します。tidx := 0、tlen := T のサイズ、n := S のサイズ、i := 0、length := 無限大、start := -1。
  2. i < n の間、以下の処理を繰り返します。
    • S[i] が T[tidx] と一致したら、tidx を1増やします。
    • tidx が tlen と等しくなったら、T 全体のマッチが完了したことを意味します。
      • end := i + 1 としてウィンドウの終端を記録します。
      • tidx を1減らし、i を減らしながら逆方向へ走査します。S[i] が T[tidx] と一致するたびに tidx を減らすことで、ウィンドウの左端(開始位置)を特定します。
      • i を1増やしてウィンドウの開始位置に合わせ、tidx も1増やして 0 に復元します。
      • end - i が現在の length より小さければ、length := end - i、start := i として最短ウィンドウを更新します。
    • i を1増やして次の文字へ進みます。

ループ終了後、start が -1 以外であれば、S[start] から S[start + length - 1] までの文字をつなぎ合わせて結果文字列 ret を構築し、それを返します。start が -1 のままなら、該当するウィンドウが存在しないため空文字列が返されます。

計算量について

T 全体のマッチが完了するたびに後方へ巻き戻す処理が発生するため、最悪計算量は O(|S| × |T|) となります。ただし、多くのケースでは非常に高速に動作します。補助配列を使用しないため、結果文字列を除く空間計算量は O(1) です。

C++による実装例

#include <bits/stdc++.h>
using namespace std;
class Solution {
    public:
    string minWindow(string S, string T) {
        int tidx = 0;
        int tlen = T.size();
        int n = S.size();
        int i = 0;
        int length = INT_MAX;
        int start = -1;
        string ret;
        while (i < n) {
            if (S[i] == T[tidx]) {
                tidx++;
                if (tidx == tlen) {
                    int end = i + 1;
                    tidx--;
                    while (tidx >= 0) {
                        if (S[i] == T[tidx]) {
                            tidx--;
                        }
                        i--;
                    }
                    i++;
                    tidx++;
                    if (end - i < length) {
                        length = end - i;
                        start = i;
                    }
                }
            }
            i++;
        }
        if (start != -1)
        for (int i = start; i < start + length; i++)
        ret += S[i];
        return ret;
    }
};
main(){
    Solution ob;
    cout << (ob.minWindow("abcdebdde", "bde"));
}

入力

"abcdebdde", "bde"

出力

"bcde"
  1. C++で解くナイトの最短移動回数問題:メモ化再帰による効率的な解法

    問題概要無限に広がるチェス盤を考えます。座標は -∞ ~ +∞ の範囲に及び、ナイトは初期状態でマス [0, 0] に配置されています。ナイトの移動は下図のように8通りあり、それぞれ「縦または横の方向に2マス、その後それと直交する方向に1マス」という動きになります。この問題では、ナイトを目標のマス [x, y] まで移動させるのに必要な最小手数を求めます。なお、必ず目的地に到達できる(解が存在する)ことが保証されています。具体例たとえば入力が x = 5、y = 5 の場合、出力は 4 になります。これは次のような経路で到達できるためです。[0,0] → [2,1] → [4,2] → [3,

  2. Windowsで使えるC++開発向けおすすめIDE 7選

    ```html 大規模なプロジェクトをプレーンなテキストエディターだけで管理するのは困難です。こうしたケースではIDE(統合開発環境)を使った方が、生産性が向上しストレスも大幅に軽減されます。IDEにはさまざまな種類があり、自分のニーズに合ったものを選ぶことが重要です。ここでは、Windowsで利用できる優れたC/C++向けIDEをご紹介します。 1. Visual Studio Microsoftが開発した定番IDEです。Windows上でのC++プログラムの構築・開発・プロファイリングにおいて、最高クラスのツール群を備えています。豊富なプラグインストアも魅力で、Azure、PowerShe