アルゴリズム理論では、速度を最適化するとメモリが消費され、その逆も同様であることがよく知られています。私のアルゴリズムはもう少し多くのメモリを使用しますが (あまり多くはありません)、代わりに大きな配列を 1 回だけスキャンします。
public static int GetOffsetOfArrayInArray(byte[] bigArray, int bigArrayOffset, int bigArrayCount, byte[] smallArray)
{
// TODO: Check whether none of the variables are null or out of range.
if (smallArray.Length == 0)
return 0;
List<int> starts = new List<int>(); // Limited number of elements.
int offset = bigArrayOffset;
// A single pass through the big array.
while (offset < bigArrayOffset + bigArrayCount)
{
for (int i = 0; i < starts.Count; i++)
{
if (bigArray[offset] != smallArray[offset - starts[i]])
{
// Remove starts[i] from the list.
starts.RemoveAt(i);
i--;
}
else if (offset - starts[i] == smallArray.Length - 1)
{
// Found a match.
return starts[i];
}
}
if (bigArray[offset] == smallArray[0] &&
offset <= (bigArrayOffset + bigArrayCount - smallArray.Length))
{
if (smallArray.Length > 1)
// Add the start to the list.
starts.Add(offset);
else
// Found a match.
return offset;
}
offset++;
}
return -1;
}
リストは、 instarts
の潜在的な開始オフセットを追跡するために使用されます。inの出現回数よりも多くの要素が含まれることはありません(リストを最適化し、メモリの再割り当ての回数を減らすために事前に計算される場合があります)。を含むのに十分なバイト数が残っていない場合は試行されず、 が見つかった場合、アルゴリズムは停止します。の最後に達すると停止します。したがって、最悪の場合の実行時間は O(1) になり、メモリ使用量は O(1) になります。smallArray
bigArray
smallArray[0]
smallArray
bigArray
smallArray
smallArray
bigArray
さらに考えられる最適化には、アンセーフ コードでのポインターの使用、およびサイズを事前に計算できる固定配列によるリストの置き換えが含まれます (前述のとおり)。ただし、リストでは間違ったオフセットが削除され (小さい内側のループ)、配列では間違ったオフセットがスキップされなければならないため (固定サイズの内側のループですが、要素へのアクセスが高速になる可能性があります)、どちらが高速かをベンチマークする必要があります。
また、大きくなると予想するかどうかも重要ですsmallArray
。その場合、while ループにチェックを追加して、starts.Length != 0 || offset <= (bigArrayOffset + bigArrayCount - smallArray.Length)
. そうしないと、ループが停止し、オカレンスが見つからない可能性があります。