ホームページ ウェブフロントエンド jsチュートリアル JavaScript のデータ構造とアルゴリズム (5): クラシック KMP アルゴリズム_JavaScript スキル

JavaScript のデータ構造とアルゴリズム (5): クラシック KMP アルゴリズム_JavaScript スキル

May 16, 2016 pm 03:54 PM
javascript kmp データ構造 アルゴリズム クラシック

KMP アルゴリズムと BM アルゴリズム

KMP はプレフィックス マッチングと BM サフィックス マッチングの古典的なアルゴリズムです。プレフィックス マッチングとサフィックス マッチングの違いは比較の順序のみであることがわかります。

前方一致とは、パターン文字列と親文字列の比較は左から右であり、パターン文字列の移動も左から右であることを意味します

サフィックスマッチングとは、パターン文字列と親文字列の比較は右から左へ、パターン文字列の移動は左から右へということを意味します。

前の章 から、BF アルゴリズムもプレフィックス アルゴリズムであることは明らかですが、1 つずつのマッチングの効率は非常に傲慢です。当然、O( について言及する必要はありません。 mn) オンラインでは迷惑な KMP が多くのことを説明していますが、基本的には高レベルの方法を採用しているので、あなたは混乱するかもしれません。私はそれを最も現実的な方法で説明しようとしました。

KMP

KMP もプレフィックス アルゴリズムの最適化バージョンです。Knuth、Morris、Pratt の 3 つの名前の略称として KMP と呼ばれる理由は、BF と比較した場合の KMP アルゴリズムの最適化ポイントです。各パターン列の移動距離を動的に調整します。BF は毎回 1、

必ずしも KMP に当てはまるわけではありません

図に示すように、BF と KMP の事前アルゴリズムの違いを比較します

写真を比較して次のことがわかりました:

文字列 T 内のパターン文字列 P を検索します。自然に 6 番目の文字 c と一致すると、第 2 レベルが不一致であることがわかります。そこで、BF メソッドは、パターン文字列 P 全体を 1 桁移動します。 KMP はそれを 2 つ移動します。

BF のマッチング方法はわかっていますが、なぜ KMP は 1 桁、3 桁、または 4 桁ではなく 2 桁を移動するのでしょうか?

前の図を説明します。パターン文字列 P が ababa に一致する場合は正しく、c に一致する場合は誤りです。KMP アルゴリズムの考え方は、ababa が正しく一致するということです。この情報を使用して、「検索位置」を比較された位置に戻すのではなく、後方に移動し続けるため、効率が向上します。

そこで問題は、移動するポジションの数をどうやって知るかということです。

このオフセット アルゴリズム KMP の作成者は、次のように要約しています。

コードをコピー コードは次のとおりです:

シフト桁 = 一致した文字の数 - 対応する部分一致値

オフセット アルゴリズムはテキスト文字列ではなく部分文字列にのみ関連するため、ここでは特別な注意を払う必要があります

では、部分文字列内の一致する文字の数と、対応する部分一致の値を理解するにはどうすればよいでしょうか?

一致する文字:

コードをコピー コードは次のとおりです:

T : アババババブ
p:ababacb

p の赤いマークは一致した文字であり、わかりやすい

部分一致値:

これは核となるアルゴリズムですが、理解するのが難しいです

場合:

コードをコピー コードは次のとおりです:

T:アロナアbbcc
P:アーロナック

このテキストを観察すると、c のマッチング時にエラーが発生した場合、次の移動は前の構造に基づいてどこに行われるでしょうか?
コードをコピー コードは次のとおりです:

アーロナBBCC
アーロナック

つまり、パターン テキスト内で、文字の特定の段落の始まりと終わりが同じ場合、自然フィルタリング中にコンテンツのこの段落をスキップできます。この考えも合理的です。

このルールを知っていると、与えられる部分一致テーブルのアルゴリズムは次のようになります:

まず、「プレフィックス」と「サフィックス」という 2 つの概念を理解する必要があります。 「プレフィックス」は、最後の文字を除く文字列の先頭のすべての組み合わせを指します。「サフィックス」は、最初の文字を除く文字列の末尾のすべての組み合わせを指します。

「部分一致値」は「prefix」と「suffix」の最も長い共通要素の長さです。

BF戦の場合のaaronaacの部門を見てみましょう

BF の変位: a,aa,aar,aaro,aaron,aarona,aaronaa,aaronaac

では、KMP の部門はどうなるのでしょうか?ここでプレフィックスとサフィックスを導入する必要があります

まず、KMP 部分一致テーブルの結果を見てみましょう:

コードをコピー コードは次のとおりです:

a a r o n a a c
[0、1、0、0、0、1、2、0]

間違いなく混乱しているので、心配しないでください。接頭辞と接尾辞を分けて説明しましょう

コードをコピー コードは次のとおりです:

一致文字列: 「Aaron」
接頭語: A、Aa、Aar、Aaro
サフィックス: aron、ron、on、n

移動位置: 実際には、一致した各文字の接頭辞と接尾辞を比較して等しいかどうかを確認し、合計の長さを計算します

部分一致テーブルの分解

KMP のマッチング テーブル アルゴリズム。p はプレフィックス、n はサフィックス、r は結果を表します

コードをコピー コードは次のとおりです:

a, p=>0, n=>0 r = 0

aa, p=>[a], n=>[a], r = a.length => 1

aar, p=>[a,aa], n=>[r,ar] ,r = 0

aaro, p=>[a,aa,aar], n=>[o,ra,aro] ,r = 0

アーロン p=>[a,aa,aar,aaro], n=>[n,on,ron,aron] ,r = 0

aarona, p=>[a,aa,aar,aaro,aaron], n=>[a,na,ona,rona,arona] ,r = a.length = 1

aaronaa, p=>[a,aa,aar,aaro,aaron,aarona], n=>[a,aa,naa,onaa,ronaa,aronaa] , r = Math.max(a.length ,aa.length) = 2

aaronaac p=>[a,aa,aar,aaro,aaron,aarona], n=>[c,ac,aac,naac,onaac,ronaac] r = 0

BF アルゴリズムと同様に、まず、一致する可能性のある各添字の位置を分解し、キャッシュします。一致する場合は、この「部分一致テーブル」を使用して、移動する必要がある桁数を特定します。

aaronaac のマッチング テーブルの最終結果は 0,1,0,0,0,1,2,0 になります。

JS バージョンの KMP は以下に実装されます。2 種類あります

KMP 実装 (1): KMP キャッシュ マッチング テーブル

KMP 実装 (2): 次の KMP を動的に計算します


KMP 実装 (1)

マッチングテーブル

KMP アルゴリズムで最も重要なのはマッチング テーブルです。マッチング テーブルが必要ない場合は、BF の実装が KMP です。

マッチング テーブルにより次の変位カウントが決定されます

上記のマッチング テーブルのルールに基づいて、kmpGetStrPartMatchValue メソッドを設計します

function kmpGetStrPartMatchValue(str) {
   var prefix = [];
   var suffix = [];
   var partMatch = [];
   for (var i = 0, j = str.length; i < j; i++) {
    var newStr = str.substring(0, i + 1);
    if (newStr.length == 1) {
     partMatch[i] = 0;
    } else {
     for (var k = 0; k < i; k++) {
      //前缀
      prefix[k] = newStr.slice(0, k + 1);
      //后缀
      suffix[k] = newStr.slice(-k - 1);
      //如果相等就计算大小,并放入结果集中
      if (prefix[k] == suffix[k]) {
       partMatch[i] = prefix[k].length;
      }
     }
     if (!partMatch[i]) {
      partMatch[i] = 0;
     }
    }
   }
   return partMatch;
  }
ログイン後にコピー

KMP のマッチング テーブル アルゴリズムの実装に従って、a->aa->aar->aaro->aaron->aarona-> は str.substring(0,私 1) アロナア-アロナック

次に、各分解のプレフィックスとサフィックスを通じて共通要素の長さを計算します

バックオフアルゴリズム

KMP もフロントエンド アルゴリズムです。BF アルゴリズムを完全に転送できます。唯一の変更点は、KMP がバックトラックするときに、マッチング テーブルを通じて次の値を計算できることです。

//子循环
for (var j = 0; j < searchLength; j++) {
  //如果与主串匹配
  if (searchStr.charAt(j) == sourceStr.charAt(i)) {
    //如果是匹配完成
    if (j == searchLength - 1) {
     result = i - j;
     break;
    } else {
     //如果匹配到了,就继续循环,i++是用来增加主串的下标位
     i++;
    }
  } else {
   //在子串的匹配中i是被叠加了
   if (j > 1 && part[j - 1] > 0) {
    i += (i - j - part[j - 1]);
   } else {
    //移动一位
    i = (i - j)
   }
   break;
  }
}
ログイン後にコピー
赤いマークは KMP の中心点です。next の値 = 一致した文字数 - 対応する部分一致値です。

完全な KMP アルゴリズム

<!doctype html><div id="test2"><div><script type="text/javascript">
 

  function kmpGetStrPartMatchValue(str) {
   var prefix = [];
   var suffix = [];
   var partMatch = [];
   for (var i = 0, j = str.length; i < j; i++) {
    var newStr = str.substring(0, i + 1);
    if (newStr.length == 1) {
     partMatch[i] = 0;
    } else {
     for (var k = 0; k < i; k++) {
      //取前缀
      prefix[k] = newStr.slice(0, k + 1);
      suffix[k] = newStr.slice(-k - 1);
      if (prefix[k] == suffix[k]) {
       partMatch[i] = prefix[k].length;
      }
     }
     if (!partMatch[i]) {
      partMatch[i] = 0;
     }
    }
   }
   return partMatch;
  }



function KMP(sourceStr, searchStr) {
  //生成匹配表
  var part     = kmpGetStrPartMatchValue(searchStr);
  var sourceLength = sourceStr.length;
  var searchLength = searchStr.length;
  var result;
  var i = 0;
  var j = 0;

  for (; i < sourceStr.length; i++) { //最外层循环,主串

    //子循环
    for (var j = 0; j < searchLength; j++) {
      //如果与主串匹配
      if (searchStr.charAt(j) == sourceStr.charAt(i)) {
        //如果是匹配完成
        if (j == searchLength - 1) {
         result = i - j;
         break;
        } else {
         //如果匹配到了,就继续循环,i++是用来增加主串的下标位
         i++;
        }
      } else {
       //在子串的匹配中i是被叠加了
       if (j > 1 && part[j - 1] > 0) {
        i += (i - j - part[j - 1]);
       } else {
        //移动一位
        i = (i - j)
       }
       break;
      }
    }

    if (result || result == 0) {
     break;
    }
  }


  if (result || result == 0) {
   return result
  } else {
   return -1;
  }
}

 var s = "BBC ABCDAB ABCDABCDABDE";
 var t = "ABCDABD";


 show('indexOf',function() {
  return s.indexOf(t)
 })

 show('KMP',function() {
  return KMP(s,t)
 })

 function show(bf_name,fn) {
  var myDate = +new Date()
  var r = fn();
  var div = document.createElement('div')
  div.innerHTML = bf_name +'算法,搜索位置:' + r + ",耗时" + (+new Date() - myDate) + "ms";
   document.getElementById("test2").appendChild(div);
 }


</script></div></div>

ログイン後にコピー

KMP(二)

第一种kmp的算法很明显,是通过缓存查找匹配表也就是常见的空间换时间了。那么另一种就是时时查找的算法,通过传递一个具体的完成字符串,算出这个匹配值出来,原理都一样

生成缓存表的时候是整体全部算出来的,我们现在等于只要挑其中的一条就可以了,那么只要算法定位到当然的匹配即可

next算法

function next(str) {
  var prefix = [];
  var suffix = [];
  var partMatch;
  var i = str.length
  var newStr = str.substring(0, i + 1);
  for (var k = 0; k < i; k++) {
   //取前缀
   prefix[k] = newStr.slice(0, k + 1);
   suffix[k] = newStr.slice(-k - 1);
   if (prefix[k] == suffix[k]) {
    partMatch = prefix[k].length;
   }
  }
  if (!partMatch) {
   partMatch = 0;
  }
  return partMatch;
}
ログイン後にコピー

其实跟匹配表是一样的,去掉了循环直接定位到当前已成功匹配的串了

完整的KMP.next算法

<!doctype html><div id="testnext"><div><script type="text/javascript">
 
  function next(str) {
    var prefix = [];
    var suffix = [];
    var partMatch;
    var i = str.length
    var newStr = str.substring(0, i + 1);
    for (var k = 0; k < i; k++) {
     //取前缀
     prefix[k] = newStr.slice(0, k + 1);
     suffix[k] = newStr.slice(-k - 1);
     if (prefix[k] == suffix[k]) {
      partMatch = prefix[k].length;
     }
    }
    if (!partMatch) {
     partMatch = 0;
    }
    return partMatch;
  }

  function KMP(sourceStr, searchStr) {
    var sourceLength = sourceStr.length;
    var searchLength = searchStr.length;
    var result;
    var i = 0;
    var j = 0;

    for (; i < sourceStr.length; i++) { //最外层循环,主串

      //子循环
      for (var j = 0; j < searchLength; j++) {
        //如果与主串匹配
        if (searchStr.charAt(j) == sourceStr.charAt(i)) {
          //如果是匹配完成
          if (j == searchLength - 1) {
           result = i - j;
           break;
          } else {
           //如果匹配到了,就继续循环,i++是用来增加主串的下标位
           i++;
          }
        } else {
         if (j > 1) {
          i += i - next(searchStr.slice(0,j));
         } else {
          //移动一位
          i = (i - j)
         }
         break;
        }
      }

      if (result || result == 0) {
       break;
      }
    }


    if (result || result == 0) {
     return result
    } else {
     return -1;
    }
  }

 var s = "BBC ABCDAB ABCDABCDABDE";
 var t = "ABCDAB";


  show('indexOf',function() {
   return s.indexOf(t)
  })

  show('KMP.next',function() {
   return KMP(s,t)
  })

  function show(bf_name,fn) {
   var myDate = +new Date()
   var r = fn();
   var div = document.createElement('div')
   div.innerHTML = bf_name +'算法,搜索位置:' + r + ",耗时" + (+new Date() - myDate) + "ms";
    document.getElementById("testnext").appendChild(div);
  }

</script></div></div>

ログイン後にコピー

git代码下载: https://github.com/JsAaron/data_structure

このウェブサイトの声明
この記事の内容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰属します。このサイトは、それに相当する法的責任を負いません。盗作または侵害の疑いのあるコンテンツを見つけた場合は、admin@php.cn までご連絡ください。

ホットAIツール

Undresser.AI Undress

Undresser.AI Undress

リアルなヌード写真を作成する AI 搭載アプリ

AI Clothes Remover

AI Clothes Remover

写真から衣服を削除するオンライン AI ツール。

Undress AI Tool

Undress AI Tool

脱衣画像を無料で

Clothoff.io

Clothoff.io

AI衣類リムーバー

Video Face Swap

Video Face Swap

完全無料の AI 顔交換ツールを使用して、あらゆるビデオの顔を簡単に交換できます。

ホットツール

メモ帳++7.3.1

メモ帳++7.3.1

使いやすく無料のコードエディター

SublimeText3 中国語版

SublimeText3 中国語版

中国語版、とても使いやすい

ゼンドスタジオ 13.0.1

ゼンドスタジオ 13.0.1

強力な PHP 統合開発環境

ドリームウィーバー CS6

ドリームウィーバー CS6

ビジュアル Web 開発ツール

SublimeText3 Mac版

SublimeText3 Mac版

神レベルのコード編集ソフト(SublimeText3)

C++ での機械学習アルゴリズムの実装: 一般的な課題と解決策 C++ での機械学習アルゴリズムの実装: 一般的な課題と解決策 Jun 03, 2024 pm 01:25 PM

C++ の機械学習アルゴリズムが直面する一般的な課題には、メモリ管理、マルチスレッド、パフォーマンスの最適化、保守性などがあります。解決策には、スマート ポインター、最新のスレッド ライブラリ、SIMD 命令、サードパーティ ライブラリの使用、コーディング スタイル ガイドラインの遵守、自動化ツールの使用が含まれます。実践的な事例では、Eigen ライブラリを使用して線形回帰アルゴリズムを実装し、メモリを効果的に管理し、高性能の行列演算を使用する方法を示します。

C++sort 関数の基礎となる原則とアルゴリズムの選択を調べる C++sort 関数の基礎となる原則とアルゴリズムの選択を調べる Apr 02, 2024 pm 05:36 PM

C++sort 関数の最下層はマージ ソートを使用し、その複雑さは O(nlogn) で、クイック ソート、ヒープ ソート、安定したソートなど、さまざまなソート アルゴリズムの選択肢を提供します。

Java 関数比較を使用して複雑なデータ構造を比較する Java 関数比較を使用して複雑なデータ構造を比較する Apr 19, 2024 pm 10:24 PM

Java で複雑なデータ構造を使用する場合、Comparator を使用して柔軟な比較メカニズムを提供します。具体的な手順には、コンパレータ クラスの定義、比較ロジックを定義するための比較メソッドの書き換えが含まれます。コンパレータインスタンスを作成します。 Collections.sort メソッドを使用して、コレクションとコンパレータのインスタンスを渡します。

改良された検出アルゴリズム: 高解像度の光学式リモートセンシング画像でのターゲット検出用 改良された検出アルゴリズム: 高解像度の光学式リモートセンシング画像でのターゲット検出用 Jun 06, 2024 pm 12:33 PM

01 今後の概要 現時点では、検出効率と検出結果の適切なバランスを実現することが困難です。我々は、光学リモートセンシング画像におけるターゲット検出ネットワークの効果を向上させるために、多層特徴ピラミッド、マルチ検出ヘッド戦略、およびハイブリッドアテンションモジュールを使用して、高解像度光学リモートセンシング画像におけるターゲット検出のための強化されたYOLOv5アルゴリズムを開発しました。 SIMD データセットによると、新しいアルゴリズムの mAP は YOLOv5 より 2.2%、YOLOX より 8.48% 優れており、検出結果と速度のバランスがより優れています。 02 背景と動機 リモート センシング技術の急速な発展に伴い、航空機、自動車、建物など、地表上の多くの物体を記述するために高解像度の光学式リモート センシング画像が使用されています。リモートセンシング画像の判読における物体検出

Javaのデータ構造とアルゴリズム: 詳細な説明 Javaのデータ構造とアルゴリズム: 詳細な説明 May 08, 2024 pm 10:12 PM

データ構造とアルゴリズムは Java 開発の基礎です。この記事では、Java の主要なデータ構造 (配列、リンク リスト、ツリーなど) とアルゴリズム (並べ替え、検索、グラフ アルゴリズムなど) について詳しく説明します。これらの構造は、スコアを保存するための配列、買い物リストを管理するためのリンク リスト、再帰を実装するためのスタック、スレッドを同期するためのキュー、高速検索と認証のためのツリーとハッシュ テーブルの使用など、実際の例を通じて説明されています。これらの概念を理解すると、効率的で保守しやすい Java コードを作成できるようになります。

58 ポートレート プラットフォームの構築におけるアルゴリズムの適用 58 ポートレート プラットフォームの構築におけるアルゴリズムの適用 May 09, 2024 am 09:01 AM

1. 58 Portraits プラットフォーム構築の背景 まず、58 Portraits プラットフォーム構築の背景についてお話ししたいと思います。 1. 従来のプロファイリング プラットフォームの従来の考え方ではもはや十分ではありません。ユーザー プロファイリング プラットフォームを構築するには、複数のビジネス分野からのデータを統合して、ユーザーの行動や関心を理解するためのデータ マイニングも必要です。最後に、ユーザー プロファイル データを効率的に保存、クエリ、共有し、プロファイル サービスを提供するためのデータ プラットフォーム機能も必要です。自社構築のビジネス プロファイリング プラットフォームとミドルオフィス プロファイリング プラットフォームの主な違いは、自社構築のプロファイリング プラットフォームは単一のビジネス ラインにサービスを提供し、オンデマンドでカスタマイズできることです。ミッドオフィス プラットフォームは複数のビジネス ラインにサービスを提供し、複雑な機能を備えていることです。モデリングを提供し、より一般的な機能を提供します。 2.58 中間プラットフォームのポートレート構築の背景のユーザーのポートレート 58

PHP データ構造: AVL ツリーのバランス、効率的で秩序あるデータ構造の維持 PHP データ構造: AVL ツリーのバランス、効率的で秩序あるデータ構造の維持 Jun 03, 2024 am 09:58 AM

AVL ツリーは、高速かつ効率的なデータ操作を保証するバランスのとれた二分探索ツリーです。バランスを達成するために、左回転と右回転の操作を実行し、バランスに反するサブツリーを調整します。 AVL ツリーは高さバランシングを利用して、ツリーの高さがノード数に対して常に小さくなるようにすることで、対数時間計算量 (O(logn)) の検索操作を実現し、大規模なデータ セットでもデータ構造の効率を維持します。

グローバルグラフ強化に基づくニュース推奨アルゴリズム グローバルグラフ強化に基づくニュース推奨アルゴリズム Apr 08, 2024 pm 09:16 PM

著者 | Wang Hao によるレビュー | Chonglou ニュース アプリは、人々が日常生活で情報ソースを入手する重要な方法です。 2010年頃、海外ニュースアプリの人気はZiteやFlipboardなどがあり、国内ニュースアプリの人気は主に4大ポータルでした。 Toutiaoに代表される新時代のニュースレコメンド商品の人気により、ニュースアプリは新時代を迎えました。テクノロジー企業に関しては、どの企業であっても、高度なニュース推奨アルゴリズム技術を習得していれば、基本的に技術レベルでの主導権と発言権を握ることになる。今日は、RecSys2023 Best Long Paper Nomination Award の論文、GoingBeyondLocal:GlobalGraph-EnhancedP を見てみましょう。

See all articles