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

C++で行列内のパターンの方向(水平・垂直)を検索する方法

この記事では、文字値で構成される行列(2次元配列)と、検索対象となるパターンが与えられたとき、そのパターンが行列の中で水平方向垂直方向のどちらに存在するのかを判定する問題を解説します。

問題の例

具体的な入力と出力を見てみましょう。

入力

mat[][] = {
    { r, a, m },
    { a, m, c },
    { w, f, t }
}
Pattern : raw

出力

vertical

この例では、パターン「raw」は1列目に上から下へ向かって「r → a → w」と並んでいるため、垂直方向に存在すると判定されます。

解法アプローチ

最も単純な解法は、行列のN行すべてに対して長さMのパターンを線形探索することです。この方法でも動作しますが、計算量が大きくなりやすいという欠点があります。

そこでより効率的なのが、文字列検索に優れたKMP(Knuth–Morris–Pratt)パターンマッチングアルゴリズムを利用する方法です。KMPを使えば、失敗関数(LPS配列)を事前に構築しておくことで、テキストの走査中に照合位置を後戻りせずに済むため、O(N + M) の時間計算量で高速な検索が可能になります。

実装の流れは以下の通りです。

  • まずLPS配列を計算する関数 calcLpsValues() を用意します。
  • searchPattern() 関数で、1行(または1列)の文字列に対してKMP検索を行い、パターンが見つかれば1を返します。
  • findPatternOrientation() 関数では、各行を横方向のテキストとして検索し、見つかれば「horizontal」を出力します。
  • 同時に、各列の文字を取り出して縦方向の文字列を作成し、検索して見つかれば「vertical」を出力します。

C++での実装例

#include<bits/stdc++.h>
using namespace std;
#define N 3

// LPS(最長接頭辞でもある接尾辞)配列を計算する関数
void calcLpsValues(char *pat, int M, int *lps) {
    int len = 0;
    int i = 1;
    lps[0] = 0;
    while (i < M) {
        if (pat[i] == pat[len]) {
            len++;
            lps[i++] = len;
        } else {
            if (len != 0)
                len = lps[len - 1];
            else
                lps[i++] = 0;
        }
    }
}

// KMPアルゴリズムによるパターン検索
int searchPattern(char *pat, char *txt) {
    int M = strlen(pat);
    int *lps = (int *)malloc(sizeof(int)*M);
    int j = 0;
    calcLpsValues(pat, M, lps);
    int i = 0;
    while (i < N) {
        if (pat[j] == txt[i]) {
            j++;
            i++;
        }
        if (j == M)
            return 1;
        else if (i < N && pat[j] != txt[i]) {
            if (j != 0)
                j = lps[j - 1];
            else
                i = i + 1;
        }
    }
    return 0;
}

// パターンの方向(水平 or 垂直)を判定する関数
void findPatternOrientation(char mat[][N], char *pat) {
    char *col = (char*) malloc(N);
    for (int i = 0; i < N; i++) {
        // 横方向(行)を検索
        if (searchPattern(pat, mat[i])) {
            cout<<"horizontal";
            return;
        }
        // 縦方向(列)の文字列を作成して検索
        for (int j = 0; j < N; j++)
            col[j] = *(mat[j] + i);
        if (searchPattern(pat, col))
            cout<<"vertical";
    }
}

int main() {
    char mat[N][N] = {{'r', 'a', 'm'},
                      {'a', 'm', 'c'},
                      {'w', 'f', 't'}};
    char pattern[] = "raw";
    cout<<"The orientation of the pattern in matrix is ";
    findPatternOrientation(mat, pattern);
    return 0;
}

実行結果

The orientation of the pattern in matrix is vertical

まとめ

このように、行列の各行と各列をそれぞれ文字列として扱い、KMPアルゴリズムでパターンを検索することで、パターンが水平方向と垂直方向のどちらに存在するかを効率的に判定できます。素朴な全探索よりも計算量を抑えられるため、大きな行列や長いパターンを扱う場合に特に有効な手法です。

  1. 【C++】行列の転置を求めるプログラムの作り方を解説

    この記事では、入力された行列の転置行列(transpose)を求めて出力するC++プログラムを紹介します。転置行列とは、元の行列の行と列を入れ替えた行列のことで、m×n の行列の転置は n×m の行列になります。 転置行列とは? 転置行列では、元の行列の第 i 行が第 i 列へ、第 j 列が第 j 行へと入れ替わります。数式で表すと、元の行列 A の要素 A[i][j] は、転置行列では A[j][i] の位置に移動します。 例えば、3×3 の行列の場合、次のように行と列が入れ替わります。 元の行列: 転置行列: 6 7 1 6 3 9 3 2

  2. C++で行列の転置を求めるプログラムの書き方【サンプルコード付き解説】

    行列とは、数値を行と列の形式に整理して並べた長方形の配列のことです。そして「転置行列」とは、元の行列の行を列に、列を行に入れ替えて作られる新しい行列を指します。転置行列のイメージ例として、次のような3×3の行列を見てみましょう。1 2 3 4 5 6 7 8 9この行列を転置すると、次のようになります。1 4 7 2 5 8 3 6 9元の行列の1行目(1, 2, 3)が、転置後には1列目になっていることが分かります。このように、元の行列の要素 a[i][j] は、転置後には a[j][i] の位置へ移動します。C++による転置行列を求めるプログラム以下が、C++で行列の転置を求めるプログラム