• ベストアンサー
※ ChatGPTを利用し、要約された質問です(原文:部分文字列の一致を検出)

効率的な部分文字列一致検出アルゴリズムの実装方法

maru_yoshi_の回答

  • ベストアンサー
回答No.11

> 文字コードをUNICODEにしても、サロゲートペアがあるので問題ないとは言えないでしょう。 なるほど。部分文字列の切り出し時に注意が必要ですね。 実際にどの程度の時間がかかるのか、試しにプログラムしてみました。 20-29文字の乱数文字列を複数作成、部分文字列の比較を総当たりで判定。 部分文字列の判定は strstr を使用した力技。 ただし、文字列の切り出しを少しだけ工夫。 OS:Windows7 Home(64ビット) SP1, コンパイラ:VC2012 update 3 部分文字列長(minChars=4) 文字列数 1000=> 1737ms 文字列数 2000=>10004ms 文字列数 3000=>13189ms 文字列数 4000=>36628ms 実行時間が文字列数の二乗に比例していない理由は不明。 誤差にしても1000,3000の場合が短すぎる。 (プログラムが間違っている?) 文字列数1000で2秒以下なので、力技で十分では? このプログラムを再利用するには、unicode対応(char=>wchar_t、サロゲートペアの処理)が必要です。 プログラムは以下の通り。 #include "stdafx.h" #include <cstdio> #include <ctime> #include <iostream> #include <string> #include <vector> #pragma warning(disable:4996) using namespace std; const size_t FileNameLengthBase = 20; // ファイル名の長さ(ベース) const size_t FileNameLengthRand = 10; const size_t FileCount = 1000; const size_t MinChars = 4; // 比較に用いる部分文字列の長さ void init(vector<string>& names) { srand(0); for (vector<string>::iterator it = names.begin(); it != names.end(); ++it) { size_t n = rand() % FileNameLengthRand + FileNameLengthBase; for (size_t i = 0; i < n; ++i) { it->push_back(char('a' + rand() % 26)); } } } bool IsPartialMatch(const char* const str1, const char* const str2, size_t minChars) { bool bResult = false; size_t len = strlen(str2); char* temp = new char[len + 1]; // 比較文字列用のバッファを準備 strcpy(temp, str2); char* last = temp + len; // 比較文字列の最後へのポインタ char* pos = last - minChars; // 比較文字列の最後のパターンへのポインタ for (; temp <= pos; --pos) // 比較文字列の後ろから比較を行う { const char* result = strstr(str1, pos); if (result != nullptr) { bResult = true; break; } *(--last) = 0; // 比較パターンの文字列を終端 } delete [] temp; return bResult; } // 文字列が一致した位置を取得する // 復帰値:文字列が一致した文字列1の位置 // 文字列が一致していない場合、戻り値は0を返す size_t GetPartialMatchPosition ( const char* const str1 , const char* const str2 , size_t minChars , size_t& nPos // 文字列2の一致した位置(出力) ) { size_t nResult = 0; size_t len = strlen(str2); char* temp = new char[len + 1]; // 比較文字列用のバッファを準備 strcpy(temp, str2); char* last = temp + len; // 比較文字列の最後へのポインタ char* pos = last - minChars; // 比較文字列の最後のパターンへのポインタ for (; temp < pos; --pos) // 比較文字列の後ろから比較を行う { const char* pResult = strstr(str1, pos); if (pResult != nullptr) { nResult = pResult - str1; nPos = pos - temp; break; } *(--last) = 0; // 比較パターンの文字列を終端 } delete [] temp; return nResult; } int _tmain(int /*argc*/, _TCHAR* /*argv[]*/) { vector<string> FileNames(FileCount); init(FileNames); int n1(0), n2(0); clock_t start = clock(); size_t MatchCount = 0; for (vector<string>::const_iterator i = FileNames.cbegin(); i != FileNames.cend(); ++i) { n2 = n1 + 1; for (vector<string>::const_iterator j = i + 1; j != FileNames.cend(); ++j) { if (IsPartialMatch(i->c_str(), j->c_str(), MinChars)) { // size_t pos2 = 0; // size_t pos1 = GetPartialMatchPosition(i->c_str(), j->c_str(), MinChars, pos2); // cout << "\"" << *i << "\":" << pos1 << "\t\"" << *j << "\":" << pos2 << endl; ++MatchCount; } ++n2; } ++n1; } clock_t end = clock(); cout << "実行時間:" << end-start << "ms" << endl; cout << "一致した数:" << MatchCount << endl; IsPartialMatch(FileNames[340].c_str(), FileNames[755].c_str(), MinChars); return 0; } 自作の高速化(10倍以上)したプログラムですが、やはり文字列数に依存します。 興味があれば公開してもいいですが。...

katorea21
質問者

お礼

提示頂いたプログラムを私の環境(Win7 x64/Core i7/16GB)で実行してみたところ、下記のような数字が出ました。概ね文字列数の2乗に比例しているようです。 文字列数 1000=>174ms 文字列数 2000=>622ms 文字列数 3000=>1430ms 文字列数 4000=>2550ms 文字列数 10000=>15768ms 上記コードでは、実行ごとに毎回同じ乱数が生成されます。たまたまその乱数のパターンによってそのような結果になったのではないでしょうか?

関連するQ&A

  • java 文字列の部分一致について

    /* 1から50まで順に数を表示する。 但し、その数が3の倍数か3の付く数字の場合、数字の後に!を表示する。 5 の倍数の場合は、数字の後に?と表示する。 両方の条件に合致した場合、数字の後に!?と表示する。 */ class Show{ public static void main(String[] args){ int i = 1;    while(i <= 50){       if(i % 3 == 0 && i % 5 ==0){        System.out.println(i + "!?");      }else if(i % 5 == 0){        System.out.println(i + "?");      }else if(i % 3 == 0){        System.out.println(i + "!");      }else{        System.out.println(i);      }      i++;   } } このような問題で、3を含む数字、とあるので、文字列の部分一致を検索する時に使用するStringクラスのindexOfを使用するのでは?と考えています。しかし、こちらはequalsで判定しますが、3の倍数は上記のコードにもある通り、==で判定しています。文字列判定と ==演算子は同じif(条件)の中には入れる事が出来ないので、じゃあどうする?という具合になってしまっています。どなたか「数が3の倍数か3の付く数字の場合」の処理を教えて頂けないでしょう?よろしくお願い致します。

    • ベストアンサー
    • Java
  • ,で句切って部分一致をファイル出力

    昨日も出したのですが自分で作成してみたんですけどヒントをいただいて作成してみたんですけど間をどうしていいかわからないので教えていただきたく載せました。 よろしくお願いいたします。 コメント部分をつくればいいみたいなのですが… (1)フォルダにファイルを用意する(CSV形式の文字列のファイル) (2)最初に文字列をキーボードから入力させる(文字列は半角で5文字まで、それ以外ならば繰り返し入力させる) (3)フォルダのファイル読み込み、(2)で入力した文字列が含まれている単語をファイルに出力(ファイルは新規作成 例: 読込み元ファイル: river,request,fire,maybe,best,over,coin,confortable, today,task,mary,face,popular,music,rock, mark,fight,replay,listen,pop, ------------------- 入力文字列:fi ファイル出力結果 fire, fight, ---------------------- 入力文字列:re ファイル出力結果 request, fire, replay public class Kadai4 { /** * @param args */ public static void main(String[] args) { // TODO 自動生成されたメソッド・スタブ String inputString; // ユーザーがキーボードから入力 // ファイルから1行読み込む String fileString =BufferedReader(FileReader); StringDetect sDetect = new StringDetect( );// ()の中に入れる new StringTokenizer( );// ()の中に入れる if(findMatch(nextToken)){ // ファイルに出す } } public class StringDetect{ private static String inputString = null // コンストラクタ public StringDetect(String str){ //ここに入れる } public static boolean findMatch(String word){ //wordとinputを比較して部分一致があれば //true、一致しなければfalseを返す } } }

    • ベストアンサー
    • Java
  • 文字列比較

    最長10文字の文字列を2件入力し、char型の配列にそれぞれ格納する。2つの文字列を比較し、文字列が同じだったら「equal」を表示し異なっていたら「Not equal」を表示するプログラムを作成せよという課題が出ました。 条件として、11文字以上の文字が入力されたら、先頭から10文字までを有効とし、11文字目以降を無視する。下記のプログラムで文字列1に11文字以上入力すると、うまく動きません。なぜ、うまくいかないかと、どうなおしたらよいかを教えてください。 #include<stdio.h> #include<string.h> #define max_length 10 void get_string (char *p_str, int size); int main() { char string1[max_length+2]; char string2[max_length+2]; printf("文字列1:"); get_string(string1,max_length+2); printf("文字列2:"); get_string(string2,max_length+2); if(!strncmp(string1,string2,max_length)) puts("equal"); else puts("Not equal"); } void get_string (char *p_str, int size) { fgets(p_str,size,stdin); }

  • Javaの文字列の大小比較についてです。

    Javaでは、文字列の大小比較をする時、StringのcompareToを使用しまが… compareToの中の処理は一旦char型に直して、それを比較しているのでしょうか? また、compareToを使用せずに、プログラム内に自分で書いた場合、処理速度は変化ありますか?

    • ベストアンサー
    • Java
  • Excel 文字列 一致

    2つの文字を比較して一致した文字の数を数える関数があれば教えてください。 例えばExcelでA1セルに「海山商事」A2セルに「海猫商事」を入れて比較し、一致している文字数(この場合は「3」)をA3に出力したいのですがどうしたらよいですか?大変恐縮ですがアドバイスください。よろしくお願いします。

  • 文字列を抜き出してきてファイルを生成

    当方はC++を用いております。文字列の部分列にアクセスして、 それを新たに文字列としたいのですが、可能でしょうか? 具体的には、英数字、空白のみからなるレコードがあったとします。 例: 01 po 0979876 7行目から10行目までをとりだします。 0979 これをひとつの文字列とします。 なお、目的は、0979という名のファイルを作ることです。

  • 文字列の動的確保とポインタ配列について

    C言語についての質問です。 現在、キーボードから文字列を読み込みファイルに保存するプログラムを作成しています。 プログラムの条件は、以下の通りです。 1: キーボードから英数字(最長でMAX_LEN(1000)-1文字)を入力して文字列(文字配列)dataに格納後、画面に表示する。 2: 入力された文字列と同じ長さの文字列を格納する領域を動的に確保し、文字列dataをその領域に コピーする。なお、必要な文字配列の長さは文字列の長さ+1バイトである。 3: 文字列endが入力されるか、入力された文字列がNUM_STRING(10)個になるまで1~2の処理を繰り返す 4: 各文字列へのポインタを格納する(char *)型ポインタの配列str_p(サイズ:NUM_STRING)を定義して利用する。 5:1~2の処理が終了した後で、メモリに格納されたすべての文字列をファイルに出力する。ファイル名はoutput.txtとし、最初の行に文字列の個数を、次の行以降に入力された順番と「逆の順番」で文字 列を出力すること。 実行例 input ->st22 st22 input->st1 st1 end ファイルの中身 2 st1 st22 現在完成しているプログラムは以下の通りです。 #include<stdio.h> #include<string.h> #include <stdilb.h> #define NUM_STRING 10 #define MAX_LEN 1000 int main (void) { int n, i; char data[MAX_LEN] = {}; char *str_p[NUM_STRING]; FILE *fp; do { printf("input->"); scanf("%s", data); if (strcmp(data, "end") == 0) { break; } else { printf("%s\n", data); n++ 2の処理 } while(n <= NUM_STRING); if ((fp = fopen("output.txt", "w")) == NULL) { fprintf(stdout, "File open error\n"); } fprintf(fp, "%d\n", n); for (i = n-1; i>0; i--) fprintf(fp, "5s\n", str_p[i]); fclose (fp); return 0; } 特に動的確保のところがよく分かりません。 回答よろしくお願いします。       

  • 配列にある文字と文字列との一致を調べたい

    PHPについては素人同然で このような質問を失礼します。 $hensu に入っている文字と $myword の文字(配列)とを比較して、 一致、不一致の処理を行いたいと考えています。 $myword にはNGのワードを複数入れたいので $myword = explode(",", "ああ,いい,うう"); という処理をするつもりです。 NGワードですので今後も増える可能性が ありますので。 このような形で処理する方法をお教えください。 何卒よろしくお願いします。

    • ベストアンサー
    • PHP
  • 文字列の中の1文字を比較するには?

    XP,Studio.NETでC++を書いています。 文字列の中の1文字を比較したいのですがどのようにしたらいいのかわかりません。 今以下のような文字列がstring[300]に入っているとします。 「\nは改行コードです。printf("");では"から"までの文字が画面に表示されます。」 このとき、1文字ずつを取り出し、文字を比較したいのですが (iを増加) if(string[i]=='\') flag=1; //処理→次にnが来る。 if(string[i]=='"') flag=2; //処理→文字はダブルコーテーション という処理をしたいのですが、 エラー:定数が多すぎます。 エラー:定数が2行目に続いています。 と出ます。どうしたらいいのでしょうか? どなたか教えていただけると幸いです。

  • 文字列

    ・文字列をキーボードから入力する関数を作成する。 書式:char *StrInp(char *pDefStr, int nLen); 引数:char *pDefStf; 初期文字列 int nLen; 入力可能文字数(1~79) 戻り値:正常ならば、入力した文字列の先頭ポインタ、エラー時はNULL。 処理:pDefStrに与えた文字列を初期値とする文字入力を行う。    nLenで指定した文字数まで入力可能とし、その範囲は1~79    までする。入力時の初期カーソル位置は与えた文字列の最後    になります。初期文字列が必要ない場合はヌル文字を与えます。    初期文字列を与えられた場合は、その文字列も更新可能とする。   ・入力の終了は「リターン」キーとする。   ・「BS」キーを押すと、カーソルの1文字前の文字前の文字を    消去する。 という、問いです。難しくてわかりません。どなたかたすけてください。