C#中的strstr()等价物

时间:2010-08-21 08:39:20

标签: c# .net arrays algorithm strstr

我有两个byte[],我希望在第一个byte[](或其中的一个范围)中找到第一个出现的第二个byte[]

我不想使用字符串来提高效率(将第一个byte[]转换为string效率不高)。

基本上我相信这是strstr()在C中的作用。

最好的方法是什么(因此效率高,易于使用)?

这应该是这样的:

int GetOffsetOfArrayInArray(byte[] bigArray, int bigArrayOffset, int bigArrayCount, byte[] smallArray);

谢谢!

更新

我想要一种比简单搜索更有效的解决方案。这意味着应该使用比较缓冲区可以更高效的事实 - memcmp()比迭代字节更有效。

另外,我知道有一些算法可以优化这样的场景:

  • 大阵列:“12312351231234”
  • 小阵列:“1231234”
  • 朴素算法: 7比较发现“1231235”不同于“1231234”,2比较找到下一个“1”,4比较发现“1235”不同于“ 1231“,3比较找到下一个”1“,7比较找到匹配。总共7 + 2 + 4 + 3 + 7 = 23比较。
  • 智能算法: 7比较发现“1231235”与“1231234”不同,直接跳到下一个“1”(不比较),4比较发现“1235”是不同于“1231”,直接跳过“5”,7比较找到匹配。总共7 + 4 + 7 = 18比较。

4 个答案:

答案 0 :(得分:3)

我没有任何代码,但您找到的最快解决方案的名称是Boyer-Moore算法。它可以比O(n)做得更好。

Here是CodeProject上字符串的实现。看起来转换为byte[]应该不会太困难。

答案 1 :(得分:1)

int GetOffsetOfArrayInArray(byte[] bigArray, int bigArrayOffset, 
                               int bigArrayCount, byte[] smallArray)
{
     byte first = smallArray[0];
     bool cont= true;
     while (cont && 
            bigArrayOffset=Array.IndexOf(bigArray, first, bigArrayOffset) != -1)
     {
         if (bigArrayOffset + smallArray.Length > bigArray.Length)
         {
              bigArrayOffset = -1;
              break;
         }
         cont= false;
         for(int i=1; i< smallArray.Length; ++i)
         {
              if (bigArray[bigArrayOffset+i] != smallArray[i])
              { 
                 ++bigArrayOffset;
                 cont = true;
                 break;
              }
         }
     }
     return bigArrayOffset;
}

更新; (希望如此)修复问题Henk提醒我。

更新2:解决原始问题的更新:

int GetOffsetOfArrayInArray(byte[] bigArray, int bigArrayOffset, 
                               int bigArrayCount, byte[] smallArray)
{
     int bigArrayEnd = Math.Min(bigArrayCount + bigArrayOffset, bigArray.Length)
     byte first = smallArray[0];
     bool cont= true;
     while (cont && 
            bigArrayOffset=Array.IndexOf(bigArray, first, bigArrayOffset) != -1)
     {
         int bookmark = bigArrauOffset + 1;
         bool bookmarkset = false;
         if (bigArrayOffset + smallArray.Length > bigArrayEnd )
         {
              bigArrayOffset = -1;
              break;
         }
         cont= false;
         for(int i=1; i< smallArray.Length; ++i)
         {
              if (!bookmarkset && bigArray[bigArrayOffset+i] == first)
              {
                   bookmark = bigArrayOffset+i;
                   bookmarkset = true;
              }
              if (bigArray[bigArrayOffset+i] != smallArray[i])
              { 
                 bigArrayOffset = bookmark;
                 cont = true;
                 break;
              }
         }
     }
     return bigArrayOffset;
}

答案 2 :(得分:0)

这是我对解决方案的看法。它分为两部分。第一部分主要是寻找潜在的开始。如果它找到一个,则比较两端的列表(降低循环计数,这基本上是一个微观优化而不是分析器,但通常它更快)

int GetOffsetOfArrayInArray(byte[] bigArray,
                        int bigArrayOffset,
                        int bigArrayCount,
                        byte[] smallArray)
    {
        var length = smallArray.Length;
        var lastPossibleStart = bigArray.Length - length;
        var startByte = smallArray[0];

        for (var first = bigArrayOffset; first < lastPossibleStart; first++)
        {
           if (bigArray[first] == startByte &&
               check(bigArray, smallArray, first, length))
           {
              return first;
           }
        }
        return -1;
    }

    bool check(byte[] bigArray, byte[] smallArray, int first, int length)
    {
        var smallIndex = 0;
        var smallLast = length - 1;
        var last = first + length - 1;
        for (var i = first; smallIndex <= smallLast; i++)
        {
            if (bigArray[i] != smallArray[smallIndex] ||
                 bigArray[last] != smallArray[smallLast])
            {
                return false;
            }
            smallIndex = i - first + 1;
            last--;
            smallLast--;
        }
        return true;
    }
}

答案 3 :(得分:0)

在算法理论中,众所周知,优化速度会耗费内存,反之亦然。我的算法使用了更多的内存(不多)但反过来只扫描一次大数组。

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

列表starts用于跟踪smallArraybigArray的潜在起始偏移量。它永远不会包含比smallArray[0]smallArray出现次数更多的元素(可以提前计算以优化列表并减少内存重新分配的数量)。如果bigArray中没有足够的字节来包含smallArray,则不会尝试,并且当找到smallArray时,算法会停止。它也会在达到bigArray结束时停止。因此,最坏的情况是运行时间为O(1),内存使用量为O(1)。

进一步可能的优化包括在不安全的代码中使用指针,并用固定数组替换列表,固定数组的大小可以提前计算(如前所述)。但是,因为在列表中错误的偏移被删除(较小的内部循环)并且在数组中必须跳过错误的偏移(固定大小的内部循环但可能更快的元素访问),您必须对哪一个更快进行基准测试。

您是否希望smallArray大或不大也很重要。执行此操作时,可以向while循环添加一个检查,检查是否starts.Length != 0 || offset <= (bigArrayOffset + bigArrayCount - smallArray.Length)。否则循环可能会停止,并且没有发现任何事件。