我有一个非常长的字节数组,例如:
Byte[] bytes = {90, 80, 63, 65, 70 ...};
这个数组的大小理论上接近20-30 Mb。有没有一种快速的方法来检查这个数组是否包含另一个数组,例如:
Byte[] small = {63, 80, 75, 77};
首先,我需要按照小数组中定义的顺序查找字节。其次,我需要在另一个数组中查找小数组,而不是任何小数组中的字节。 提前感谢所有人。
对于性能,您希望使用类似Boyer-Moore字符串搜索算法的东西。虽然它是为字符串设计的,但在字节数组上应该同样有效,并且比暴力解决方案要快得多。
维基百科文章提供了几种实现方式,包括Java和C中的一种,因此创建一个C#实现应该相当轻松。
事实证明,将维基百科文章中的Java实现转换为C#(并将char
转换为byte
)确实很容易。以下是代码:
public static class BoyerMoore
{
public static int IndexOf(byte[] haystack, byte[] needle)
{
if (needle.Length == 0)
{
return 0;
}
int[] charTable = MakeCharTable(needle);
int[] offsetTable = MakeOffsetTable(needle);
for (int i = needle.Length - 1; i < haystack.Length;)
{
int j;
for (j = needle.Length - 1; needle[j] == haystack[i]; --i, --j)
{
if (j == 0)
{
return i;
}
}
i += Math.Max(offsetTable[needle.Length - 1 - j], charTable[haystack[i]]);
}
return -1;
}
private static int[] MakeCharTable(byte[] needle)
{
const int ALPHABET_SIZE = 256;
int[] table = new int[ALPHABET_SIZE];
for (int i = 0; i < table.Length; ++i)
{
table[i] = needle.Length;
}
for (int i = 0; i < needle.Length - 1; ++i)
{
table[needle[i]] = needle.Length - 1 - i;
}
return table;
}
private static int[] MakeOffsetTable(byte[] needle)
{
int[] table = new int[needle.Length];
int lastPrefixPosition = needle.Length;
for (int i = needle.Length - 1; i >= 0; --i)
{
if (IsPrefix(needle, i + 1))
{
lastPrefixPosition = i + 1;
}
table[needle.Length - 1 - i] = lastPrefixPosition - i + needle.Length - 1;
}
for (int i = 0; i < needle.Length - 1; ++i)
{
int slen = SuffixLength(needle, i);
table[slen] = needle.Length - 1 - i + slen;
}
return table;
}
private static bool IsPrefix(byte[] needle, int p)
{
for (int i = p, j = 0; i < needle.Length; ++i, ++j)
{
if (needle[i] != needle[j])
{
return false;
}
}
return true;
}
private static int SuffixLength(byte[] needle, int p)
{
int len = 0;
for (int i = p, j = needle.Length - 1; i >= 0 && needle[i] == needle[j]; --i, --j)
{
len += 1;
}
return len;
}
}
static int search(byte[] haystack, byte[] needle)
{
for (int i = 0; i <= haystack.Length - needle.Length; i++)
{
if (match(haystack, needle, i))
{
return i;
}
}
return -1;
}
static bool match(byte[] haystack, byte[] needle, int start)
{
if (needle.Length + start > haystack.Length)
{
return false;
}
else
{
for (int i = 0; i < needle.Length; i++)
{
if (needle[i] != haystack[i + start])
{
return false;
}
}
return true;
}
}
如果你有数百万个字节元素,我建议:
因此
bytes.Sort(); // only need to do this once.
bool smallContained = ContainsAll(bytes, small);
并且
static bool ContainsAll(int[] src, int [] subset)
{
foreach(var i in subset)
if (src.BinarySearch(i) < 0)
return false;
return true;
}
如果我理解正确,您想要判断small
是否为bytes
的子序列。您可以通过循环遍历bytes
来实现。由于处理器缓存的优势,它应该运行非常快。
for (int i = 0, index = 0; i < bytes.Length; ++i)
if (bytes[i] == small[index]) {
if (++index >= small.Length) {
return true;
}
}
return false;
您可以使用此函数,来自Reddit帖子:
public static bool CheckSequence<T>(IEnumerable<T> containingArray, IEnumerable<T> containedArray)
{
bool result = false;
for (int i = 0; i <= containingArray.Count(); i++)
{
if (containedArray.SequenceEqual(containingArray.Skip(i).Take(containedArray.Count())))
result = true;
}
return result;
}
喜欢:
var result = CheckSequence(bytes, small);
子字符串搜索
(即使在你所使用的任何语言中都不涉及字符串),你应该能够查找到很多相关算法。 - Damien_The_UnbelieverIsSubsetOf
方法,如此答案所述。 - Farhad Jabiyev