【问题标题】:Search for an Array or List in a List在列表中搜索数组或列表
【发布时间】:2013-04-20 00:03:29
【问题描述】:

List<byte> lbyte 

byte[] searchBytes

如何在 lbyte 中不仅搜索单个字节,还搜索 searchBytes 的索引?
例如

Int32 index = lbyte.FirstIndexOf(searchBytes);

这是我想出的蛮力。
不是我正在寻找的性能。

public static Int32 ListIndexOfArray(List<byte> lb, byte[] sbs)
{
    if (sbs == null) return -1;
    if (sbs.Length == 0) return -1;
    if (sbs.Length > 8) return -1;
    if (sbs.Length == 1) return lb.FirstOrDefault(x => x == sbs[0]);
    Int32 sbsLen = sbs.Length;
    Int32 sbsCurMatch = 0;
    for (int i = 0; i < lb.Count; i++)
    {
        if (lb[i] == sbs[sbsCurMatch])
        {
            sbsCurMatch++;
            if (sbsCurMatch == sbsLen)
            {
                //int index = lb.FindIndex(e => sbs.All(f => f.Equals(e)));  // fails to find a match
                IndexOfArray = i - sbsLen + 1;
                return;
            }
        }
        else 
        {
            sbsCurMatch = 0;
        }
    }
    return -1;
}

【问题讨论】:

  • @YairNevet 字节列表
  • @YairNevet 另一个字节列表,这本质上是orderedlist.containssequence 这里必须有一个很好的解决方案,因为它基本上是用string.contains 解决的,但这是一个更通用的情况
  • 尝试寻找here 的扩展方法来执行此操作。
  • 你确定你的暴力破解方法是正确的吗?看起来它没有检查搜索字符串的最后一个字符。所以在寻找“frob”时,如果只检查“fro”。我想你想增加sbsCurMatch 长度检查。就目前而言,它会在一个字符的字符串上失败。

标签: c# .net linq list


【解决方案1】:

蛮力总是一种选择。尽管与其他一些方法相比速度较慢,但​​在实践中通常并不算太糟糕。如果lbyte 不是很大并且没有病理数据,那么它很容易实现并且完全可以接受。

brute force string searching是同一个概念。

【讨论】:

    【解决方案2】:

    您可能会发现Boyer-Moore algorithm 在这里很有用。将您的列表转换为数组并搜索。算法代码取自this post

    static int SimpleBoyerMooreSearch(byte[] haystack, byte[] needle)
    {
        int[] lookup = new int[256];
        for (int i = 0; i < lookup.Length; i++) { lookup[i] = needle.Length; }
    
        for (int i = 0; i < needle.Length; i++)
        {
            lookup[needle[i]] = needle.Length - i - 1;
        }
    
        int index = needle.Length - 1;
        var lastByte = needle.Last();
        while (index < haystack.Length)
        {
            var checkByte = haystack[index];
            if (haystack[index] == lastByte)
            {
                bool found = true;
                for (int j = needle.Length - 2; j >= 0; j--)
                {
                    if (haystack[index - needle.Length + j + 1] != needle[j])
                    {
                        found = false;
                        break;
                    }
                }
    
                if (found)
                    return index - needle.Length + 1;
                else
                    index++;
            }
            else
            {
                index += lookup[checkByte];
            }
        }
        return -1;
    }
    

    然后您可以像这样搜索。如果lbyte 会在一段时间后保持不变,您只需将其转换为数组并传递即可。

    //index is returned, or -1 if 'searchBytes' is not found
    int startIndex = SimpleBoyerMooreSearch(lbyte.ToArray(), searchBytes);
    

    根据评论更新。这是IList 实现,这意味着数组和列表(以及任何其他实现IList 的东西都可以传递)

     static int SimpleBoyerMooreSearch(IList<byte> haystack, IList<byte> needle)
     {
        int[] lookup = new int[256];
        for (int i = 0; i < lookup.Length; i++) { lookup[i] = needle.Count; }
    
        for (int i = 0; i < needle.Count; i++)
        {
            lookup[needle[i]] = needle.Count - i - 1;
        }
    
        int index = needle.Count - 1;
        var lastByte = needle[index];
        while (index < haystack.Count)
        {
            var checkByte = haystack[index];
            if (haystack[index] == lastByte)
            {
                bool found = true;
                for (int j = needle.Count - 2; j >= 0; j--)
                {
                    if (haystack[index - needle.Count + j + 1] != needle[j])
                    {
                        found = false;
                        break;
                    }
                }
    
                if (found)
                    return index - needle.Count + 1;
                else
                    index++;
            }
            else
            {
                index += lookup[checkByte];
            }
        }
        return -1;
    }
    

    由于数组和列表实现了 IList,因此在您的情况下调用它时无需进行转换。

    int startIndex = SimpleBoyerMooreSearch(lbyte, searchBytes);
    

    【讨论】:

    • 您可以使用ILists 切换出您的字节数组,以使您的代码更通用
    • Unfotunalely 列表会随着每次通话而改变。我还没有遵循这个,但我会试一试。如果您实现 IList 版本,请在此处发布。
    • @Blam - 我添加了 IList 版本。它不是完全通用的(只接受字节),但它应该在你的情况下工作,因为它处理字节列表和数组。
    • 我太累了,现在不能继续,但我会测试一下。
    • @Blam:B-M 不是蛮力算法。 B-M 的要点是,当发生不匹配时,您最多可以跳过 m 个字符(字节),您的代码始终只跳过 1,这使其速度变慢了一个数量级。由于您的案例基本上是子字符串搜索(即顺序很重要,值不是唯一的,未排序),因此 B-M 是最快的解决方案。
    【解决方案3】:

    使用 lambda 表达式的另一种方法

    int index = lbyte.FindIndex(e => searchBytes.All(i => i.Equals(e));
    

    【讨论】:

    • Missing ) 我测试它没有找到匹配项。
    • 请你写你的测试用例好吗?
    • 我将测试它的位置添加到我在问题中发布的代码中。我真的没有“测试用例”来生成数据。我正在从数据库中读取这些字节。但我知道蛮力匹配得当。
    猜你喜欢
    • 2015-02-12
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-03-19
    • 2019-08-31
    • 2021-05-15
    相关资源
    最近更新 更多