如何检查bytes数组是否包含另一个数组

时间:2016-01-26 07:23:52

标签: c# arrays algorithm

我有一个很长的字节数组,例如:

Byte[] bytes = {90, 80, 63, 65, 70 ...};

它接近20-30 Mb(理论上)。有没有快速的方法来检查此数组是否包含另一个数组,例如:

Byte[] small = {63, 80, 75, 77};

首先,我需要按顺序查找字节,它们是在小数组中定义的。其次,我需要在另一个数组中找到数组而不是任何小数组的字节。 谢谢大家的进步。

6 个答案:

答案 0 :(得分:2)

对于性能,你需要像Boyer-Moore string search algorithm这样的东西。虽然它是为字符串设计的,但它在字节数组上应该也能正常工作,并且比蛮力解决方案 更高效。

维基百科的文章提供了几个实现,包括Java中的一个和C中的一个,因此创建C#实现应该相当轻松。

事实证明,将维基百科文章的Boyer-Moore的Java实现翻译成C#(以及charbyte)确实很轻松。这是:

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;
    }
}

该算法在开始时花费一个线性时间点,构建其表格;从那以后,它的速度非常快。

答案 1 :(得分:1)

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;
    }
}

答案 2 :(得分:1)

使用此:

bool isSubset = !t2.Except(t1).Any();

来自@Farhad Jabiyev的链接: Check whether an array is a subset of another

答案 3 :(得分:0)

如果我理解正确,您想说small是否是bytes的子序列。你可以通过字节循环找到它。由于处理器缓存,它应该运行得非常快。

  for (int i = 0, index = 0; i < bytes.Length; ++i) 
    if (bytes[i] == small[index]) {
      if (++index >= small.Length) {
        return true; 
      }
    }
  return false;

答案 4 :(得分:0)

你可以使用这个函数,来自this Reddit post

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);

答案 5 :(得分:-1)

如果你有数百万字节的元素,我建议:

  1. 排序字节
  2. foreach in small if if not find element return false
  3. 因此

    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;
    }