記事のディレクトリ
概念
文字列内のb文字列の位置を一致させます
さまざまなアルゴリズムの比較
素朴なアルゴリズム
上の図に示すように、スライディングウィンドウはstr2をスケートボードとして扱い、左から右にスライドして各文字列を順番に比較するという考え方です。
javascriptの実装
/**
* @param {string} haystack
* @param {string} needle
* @return {number}
*/
var strStr = function(haystack, needle) {
if (needle === '') return 0;
let l = haystack.length;
let n = needle.length;
for (let i = 0; i < l - n + 1; i++) {
if (haystack.substring(i, i + n) === needle) {
return i;
}
}
return -1;
};
ラビン-カープアルゴリズム
ホーナーの法則
アルゴリズムのアイデアは次のようなものです
- 文字列を10進数に変換できます。これらはすべて特定の数値で
基数d
表されますが、変換された数値が大きすぎる可能性があるため、1つの演算を质数
実行し求模
ます。 - スライディングウィンドウ(上記のナイーブアルゴリズムを参照)は、各部分文字列のモジュラス値を比較します----->ホーナーの法則により、次の値は前の値o(1)に基づいて計算できます。
- モジュラスが同じ場合、誤ったヒットポイントがあるため、合同な比較が必要です
なぜ素数を選ぶのですか?
モジュラスが素数であり、10qがコンピューターのワード長と正確に一致する場合は、単精度算術演算を使用して必要な演算を実行できます。
javascriptの実装
var strStr = function (haystack, needle) {
const Q = 101;//素数
const D = 256;//基数
const N = haystack.length;
const M = needle.length;
let hashHaystackt = 0; //初始化
let hashNeedle = 0;
let h = 1;
for (let i = 0; i < (M - 1); i++) {
h = (h * D) % Q;//计算 d的m-1次方 mod q的值
for (let i = 0; i < M; i++) {
hashNeedle = (D * hashNeedle + needle[i].charCodeAt(0)) % Q;
hashHaystackt = (D * hashHaystackt + haystack[i].charCodeAt(0)) % Q;
}
for (let i = 0; i <= N - M; i++) {
if (hashNeedle === hashHaystackt) {
if (haystack.substring(i - M, i) === needle) {
return i - M
}
}
if (i < N - M) {
//计算下一个hashHaystackt
hashHaystackt = (D * (hashHaystackt - haystack[i].charCodeAt(0) * h) + haystack[i + M].charCodeAt(0)) % Q;
if (hashHaystackt < 0) {
hashHaystackt += Q;
}
}
}
}
};
有限オートマトン
シーケンシャルマシンとも呼ばれる有限オートマトンは、有限離散デジタルシステムの抽象的な数学モデルです。有限オートマトンMは、5タプル(X、Y、S、δ、λ)で与えられます。ここで、X、Y、およびSはすべて空でない有限集合であり、の入力集合、出力集合、および状態集合と呼ばれます。それぞれM;δはMの次の状態関数と呼ばれる直積集合S×XからSへのマッピングです;λはMの出力関数と呼ばれるS×XからYへの単一値マッピングです。δが単一値のマッピングである場合、Mは決定性有限オートマトンと呼ばれます。δが複数値のマッピングである場合、Mは非決定性有限オートマトンと呼ばれます。有限オートマトンには、入力シーケンスを出力シーケンスに変換するシーケンスコンバーターとして、入力シーケンスに特定のプロパティがあるかどうかを識別するシーケンスレコグナイザーとして、必要なプロパティを持つシーケンスを生成するシーケンスジェネレーターとしての3つの機能があります。
この多数の定義を理解するのは困難です。このリートコードのトピックで見ることができます。簡単な理解は、各入力が異なる状態間をジャンプすることです。
実際、文字列照合は、生成にmap表
多くの計算が必要なため、適切なアルゴリズムではないと個人的に感じています。
その中で、文字列は
∑
有限のアルファベットであり、その各要素は入力記号と呼ばれ、
S
有限の状態のセットです。。要素は状態と呼ばれます。これ
f
は遷移関数であり、上記から単一値のマッピングを定義します。つまり、現在の状態がpであることを示します。入力シンボルがaの場合、次の状態qに遷移します。 、これはp後続状態と呼ばれ
s0
、一意の初期状態で
Z
あり、一連の最終状態です。
状態遷移の各ステップで、有限オートマトンの現在の状態とそれが直面する入力シンボルに従って、有限オートマトンの次の状態を一意に決定できます。つまり、転送関数の値は一意です。上記の状態遷移図に反映されています。つまり、任意のノードにn個の出力エッジがあり、これらの出力エッジのマークが同じではない場合、上記の方法で定義された有限オートマトンを決定性と呼ぶのはこのためです。有限オートマトン
偽のコード
javascriptの実装
これにより、状態マップテーブルの作成の実装は非常に簡単になります。
function createStatusMap(needle) {
// let wordmap = 'abcdefghijklmnopqrstuvwxyz';
let wordmap = 'abc';
let len = needle.length;
let wordmapLength = wordmap.length;
let map = [];
for (let i = 0; i < len + 1; i++) {
let template = needle.substr(0, i); // i 状态下已存在字符
map.push([]);
for (let j = 0; j < wordmapLength; j++) {
//遍历输入下一个字符
let status = i;
let output = template + wordmap[j] //组成新字符串
let k = template.length;
for (let q = 0; q < k + 1; q++) {
//循环匹配 output的后缀 和 needle的前缀 改变成相应的状态
if (output.substring(q, k + 2) == needle.substring(0, k + 1 - q)) {
status = i + 1 - q;
break
}else{
status = 0
}
}
map[i][j] = status; //输出到map表
}
}
return map
}
console.log(createStatusMap('ababaca'))
Knuth-Morris-Prattアルゴリズム
テキストaaaabaac(text)aaab(pattern)
を通常のマッチングに従って照合するとします.4番目の位置に一致する場合aaaa aaabは明らかに一致しませんが、すでに3桁
に一致しているため指针
、テキストとパターンの両方をロールする必要があります戻る3。この処理は、場合によっては非常に時間がかかります。
したがって、テーブルを事前に計算して直接知る
在已经匹配n位下时 只需要重新匹配pattern的某一段即可满足要求
ことができるため、テキスト側のポインターがパターンをロールバックしたり、毎回再照合したりする必要がありません。
このテーブルは、iが一致しない(つまり一致しない)位置でi + 1を一致させる場合、一致するπ文字が存在する必要があります。
たとえば、たとえば、i = 5 ababaおよびababacaに一致
ababa
する接尾辞には、少なくとも3つのyesababaca
プレフィックスがあります。これは、少なくとも3文字が一致したことを意味します。
javascriptの実装
//核心就在于创建这张表
function createMap(pattern) {
let len = pattern.length;
let map = [...Array(len)].map(() => 0); //初始化map;
let q = 1; //指针
let k = 0; //指针
for (; q < pattern.length; q++) {
while (k > 0 && pattern[q] !== pattern[k]) {
k = map[k - 1];
}
if (pattern[q] === pattern[k]) {
//相等最长公共+1
k++;
}
map[q] = k;
}
return map
}