Эквивалент strstr () в C # - PullRequest
       35

Эквивалент strstr () в C #

3 голосов
/ 21 августа 2010

У меня есть два byte[], и я хочу найти первое вхождение второго byte[] в первый byte[] (или диапазон в нем).

Я не хочу использовать строки для эффективности (перевод первого byte[] в string будет неэффективным).

В основном я верю, что именно это strstr() делает в C.

Каков наилучший способ сделать это (чтобы он был эффективным и простым в использовании)?

Вот как это должно выглядеть:

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

Спасибо!

UPDATE:

Мне нужно решение, которое было бы более эффективным, чем простой поиск. Это означает, что следует использовать тот факт, что сравнение буферов может быть более эффективным - 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 ]

3 голосов
/ 21 августа 2010

У меня нет кода для вас, но название самого быстрого решения, которое вы найдете, это алгоритм Boyer-Moore Это может быть лучше, чем O (n).

Здесь - реализация строк в CodeProject. Похоже, преобразование в byte[] не должно быть слишком сложным.

1 голос
/ 21 августа 2010
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;
}

ОБНОВЛЕНО; (надеюсь) Исправленная проблема, о которой меня предупредил Хенк.

ОБНОВЛЕНИЕ 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;
}
0 голосов
/ 21 августа 2010

В теории алгоритмов хорошо известно, что оптимизация для скорости стоит памяти и наоборот.Мой алгоритм использует немного больше памяти (не так много), но взамен сканирует только большой массив один раз.

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 используется для отслеживания возможных начальных смещений smallArray в bigArray,Он никогда не будет содержать больше элементов, чем число вхождений smallArray[0] в smallArray (которое может быть рассчитано заранее для оптимизации списка и уменьшения количества перераспределений памяти).Когда в bigArray осталось недостаточно байтов, чтобы содержать smallArray, он не пробуется, а когда был найден smallArray, алгоритм останавливается.Он также останавливается, когда достигнут конец bigArray.Поэтому наихудшее время выполнения будет O (1), а использование памяти будет O (1).

Дальнейшие возможные оптимизации включают использование указателей в небезопасном коде и замену списка фиксированным массивом,Размер можно рассчитать заранее (как отмечалось ранее).Однако, поскольку в списке удалены неправильные смещения (меньший внутренний цикл), а в массиве должны быть пропущены неправильные смещения (внутренний цикл фиксированного размера, но, возможно, более быстрый доступ к элементу), вам нужно будет определить, какое из них быстрее.

Также имеет значение, ожидаете ли вы, что smallArray будет большим или нет.Когда вы это сделаете, вы можете добавить проверку в цикл while, который проверяет, является ли starts.Length != 0 || offset <= (bigArrayOffset + bigArrayCount - smallArray.Length).В противном случае цикл может прекратиться, и вхождения не были найдены.

0 голосов
/ 21 августа 2010

Вот мое решение. Это разделено на две части. Первая часть в основном ищет потенциальный старт. Если он находит его, он сравнивает список с обоих концов (чтобы уменьшить количество циклов, которое по сути является микрооптимизацией без профилировщика, но обычно это быстрее)

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