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

アナグラムパターン検索アルゴリズムの解説とC++実装例

アナグラムとは、ある文字列やパターンに含まれる文字を並べ替えることで作れる、すべての順列のことを指します。通常のパターン検索では、パターンそのものと完全に一致する部分のみを探します。一方、アナグラムパターン検索はこれとは少し異なり、テキスト中に現れる「指定パターンのあらゆる並べ替え」をすべて検索対象とします。

アナグラムパターン検索の基本的な考え方

この問題を解くためには、テキスト全体を「パターンと同じ長さのウィンドウ」に分割して考えます。まず、パターンに含まれる各文字の出現回数を数え、配列に記録します。次に、各ウィンドウについても同じように出現頻度配列を作成し、両方の配列が一致しているかどうかを比較します。両者が一致していれば、その位置にパターンのアナグラムが存在すると判断できます。

アナグラムパターン検索アルゴリズムの時間計算量は O(n) です。これは、各ウィンドウの比較が固定サイズ(26種類の英字)の配列同士の比較であるため、定数時間とみなせるからです。

入力と出力

入力:
メイン文字列 "AABAACBABBCABAABBA"、パターン "AABC"

出力:
Anagram found at position: 2
Anagram found at position: 3
Anagram found at position: 4
Anagram found at position: 10

アルゴリズム

anagramSearch(text, pattern)

入力 − メイン文字列とパターン

出力 − パターンおよびそのすべてのアナグラムが見つかった位置の一覧

Begin
    patternFreq 配列と stringFreq 配列を定義する
    patLen := パターンの長さ
    stringLen := テキストの長さ
    patternFreq 配列の全要素を 0 で初期化する

    パターンに含まれるすべての文字に対して、
        出現頻度を 1 増やす
    done

    i := 0 から i <= stringLen - patLen まで繰り返す
        stringFreq の全要素を 0 で初期化する
        各ウィンドウに含まれるすべての文字に対して、
            出現頻度を 1 増やす
        done

        もし stringFreq と patternFreq が一致したならば、
            その位置 i にアナグラムが見つかったとして表示する
    done
End

C++による実装例

#include<iostream>
#include<cstring>
#define LETTER 26
using namespace std;

bool arrayCompare(int *array1, int *array2, int n) {
    for(int i = 0; i<n; i++) {
        if(array1[i] != array2[i])
            return false; // 1つでも不一致があれば処理を中断
    }
    return true; // 両配列は同一
}

void setArray(int *array, int n, int value) {
    for(int i = 0; i<n; i++)
        array[i] = value; // 配列の全要素に値を設定
}

void anagramSearch(string mainString, string patt, int *array, int *index) {
    int strFreq[LETTER], pattFreq[LETTER];
    int patLen = patt.size();
    int stringLen = mainString.size();
    setArray(pattFreq, LETTER, 0);      // すべての頻度を0で初期化

    for(int i = 0; i<patLen; i++) {
        int patIndex = patt[i] - 'A';   // 'A'のASCIIコード値を差し引く
        pattFreq[patIndex]++;           // 頻度を増加
    }

    for(int i = 0; i<=(stringLen - patLen); i++) {  // ウィンドウの移動範囲
        setArray(strFreq, LETTER, 0);       // メイン文字列用の頻度配列を0で初期化
        for(int j = i; j<(i+patLen); j++){  // 各ウィンドウごとの頻度を更新
            int strIndex = mainString[j] - 'A';
            strFreq[strIndex]++;            // 頻度を増加
        }

        if(arrayCompare(strFreq, pattFreq, LETTER)) {  // 両配列が一致した場合
            (*index)++;
            array[*index] = i;              // i番目の位置でアナグラムを発見
        }
    }
}

int main() {
    string mainStrng = "AABAACBABBCABAABBA";
    string pattern = "AABC";
    int matchLocation[mainStrng.size()];
    int index = -1;
    anagramSearch(mainStrng, pattern, matchLocation, &index);

    for(int i = 0; i<=index; i++) {
        cout << "Anagram found at position: " << matchLocation[i] << endl;
    }
}

出力結果

Anagram found at position: 2
Anagram found at position: 3
Anagram found at position: 4
Anagram found at position: 10
  1. データ構造入門:最適二分探索木(Optimal BST)で検索コストを最小化する方法

    最適二分探索木とはソートされた順序で整数のキー集合が与えられ、同時に各キーの出現頻度を格納した配列 freq も渡されます。この課題は、これらのデータをもとに二分探索木(BST)を構築し、すべての検索にかかるコストの合計を最小にすることです。検索コストは「キーの深さ × 出現頻度」の総和で表されます。そのため、頻度の高いキーほど根に近い浅い位置へ配置できれば、全体のコストを大きく抑えられます。このような木を最適二分探索木(Optimal BST)と呼びます。部分問題の解を保存し、ボトムアップ方式で問題を解決するために、補助配列 cost[n][n] を作成します。このコスト行列には、動的計画法

  2. Pythonでアナグラム部分文字列を検索する方法【スライディングウィンドウで実装】

    はじめに 本記事では、「テキスト中からパターンとそのアナグラム(文字の並べ替え)をすべて検索する」という問題を、Pythonで解く方法を解説します。 問題の定義 問題文: テキストとパターンが与えられます。このとき、テキスト内に出現するパターン本体だけでなく、その並べ替え(アナグラム)すべての出現位置を出力してください。 たとえば、パターンが「TOR」であれば、「ROT」「OTR」「RTO」なども同じ文字構成を持つため、すべて検索対象となります。 アルゴリズムのポイント この問題はスライディングウィンドウ(滑動窓)の考え方を使うと効率的に解けます。手順は以下のとおりです。 パターンの各文