JavaScriptで部分列の組み合わせから文字列を作れるか判定する方法
問題の概要
JavaScriptで、2つの文字列(第一引数をstr1、第二引数をstr2と呼びます)を受け取る関数を書くことを考えてみましょう。この関数の目的は、str1の中に部分列(subsequence)としてstr2が含まれているかどうかを判定することです。
ここでいう部分列とは、元の文字列からいくつかの文字をスキップすることは許されるが、選び出した文字同士の並び順は維持しなければならないというルールのもとで組み合わせた文字列のことです。
具体例
たとえば、入力として次のような文字列が与えられたとします。
const str1 = 'desxooajmepwele'; const str2 = 'example';
このときの出力は次のようになります。
const output = true;
これは、「example」という文字列が、str1から一部の文字を選び出し、その順序を保ったまま組み合わせることで構築できるためです。
実装コード
この判定処理を実現するコードは以下の通りです。
const str1 = 'desxooajmepwele';
const str2 = 'example';
const containsString = (str1 = '', str2 = '') => {
let [foundAt, next] = [0, 0];
for(const char of str2){
next = str1.slice(foundAt).indexOf(char);
if (next === - 1){
return false;
};
foundAt += next + 1;
};
return true;
};
console.log(containsString(str1, str2));実行結果
コンソールには次のように表示されます。
true
コードの解説
このアルゴリズムの仕組みを簡単に整理してみましょう。
- foundAt: 次に探索を開始するstr1内の位置(インデックス)を記録する変数です。
- next: 現在注目しているstr2の文字が、str1の残りの範囲のどこに存在するかを示すインデックスです。
- str2の各文字について順番に、str1の未探索領域(
slice(foundAt))の中から該当する文字を検索します。 - 文字が見つからなかった場合(
indexOfが-1を返した場合)は、その時点でfalseを返して処理を終了します。 - 見つかった場合は、
foundAtを見つけた位置の次まで進めます。これにより、以降の文字は必ずそれより後ろから探されることになり、文字の順序が維持されることが保証されます。 - str2のすべての文字が見つかれば、最終的に
trueを返します。
このアプローチでは、再帰処理やバックトラックが不要で、追加のメモリ消費もほとんどありません。最悪でもO(len1 × len2)程度の計算量に収まるため、シンプルながら実用性の高い部分列判定の実装と言えます。
-
JavaScript DataView()とは?ArrayBufferのバイナリデータを読み書きする方法
JavaScript の DataView は、ArrayBuffer(バイナリデータ)に対して、さまざまな数値型の読み書きを行うための低レベルインターフェースを提供するオブジェクトです。DataView を使うことで、1バイト単位で細かく制御しながら、Int16、Int32、Float64 など複数の数値型として同じバッファにアクセスできます。なお、ArrayBuffer はそのままでは直接操作できないため、DataView や TypedArray を介してアクセスする必要があります。DataView の主なメソッドsetInt16(offset, value):指定したオフセット位置に
-
JavaScriptのletキーワードとは?ブロックスコープ変数の宣言方法を解説
letキーワードとは 2015年(ECMAScript 2015/ES6)に導入されたletキーワードは、ブロックスコープを持つ変数を宣言するためのものです。letで宣言した変数は、それを囲む波括弧 { } の内側でのみ有効となり、ブロックの外からアクセスしようとするとエラーが発生します。 varとの主な違い スコープ: varは関数スコープですが、letはブロックスコープになります。 再宣言: 同じスコープ内であればvarによる再宣言は可能ですが、letで再宣言するとSyntaxErrorになります。 宣言前の参照: letで宣言した変数は、宣言より前にアクセスするとReferenceE