2010-08-21 3 views
3

의 strstr()에 두 개의 byte[]이 있는데 첫 번째 byte[] (또는 그 범위)에 두 번째 byte[]의 첫 번째 항목을 찾고 싶습니다.strstr() C#

효율성을 위해 문자열을 사용하고 싶지 않습니다. 첫 번째 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 "1231234"가 "1231234"와 다른 경우 "비교하지 않고"다음 "1"로 바로 이동하고, 4는 "1235"가 "1231" , "5"를 넘어 직접 점프, 7 경기를 비교합니다. 총 7 + 4 + 7 = 18이 비교됩니다.
+3

먼저 변환 코드를 작성한 다음 프로파일 링하십시오. 성능 병목 현상이있는 것으로 판단되면 변환을하지 않도록 최적화하십시오. 수표를 함수 호출로 감싸서 나중에 구현을 대체해야 할 경우 간단하게 구현할 수 있습니다. –

+1

memcmp()는 strstr()에 대한 대안이 아닌 * *입니다. 표준 CRT에는 찾고있는 기능이 없습니다. 모든 버전의 Windows에서 사용할 수있는 msvcrt.dll에서 strstr()을 pinvoke 할 수 있습니다. 그러나 0을 포함하는 바이트 []를 허용하지 않습니다. –

+1

"memcmp()는 단어 크기 비교를 수행하거나 하드웨어의 특수 기능을 사용하기 위해 표지 아래 구현 된 경우에만 바이트를 반복하는 것보다 효율적입니다. 입력 버퍼가 다른 정렬에서 시작하는 memcmp를 최적화하려고 시도하는 것은 꽤 ... 피팅입니다. –

답변

3

Boyer-Moore 알고리즘이다. 그것은 O (n)보다 더 잘할 수 있습니다.

Here은 CodeProject의 문자열에 대한 구현입니다. byte[]으로의 전환이 너무 어려워서는 안됩니다.

+0

.NET에서이 구현은 훌륭합니다. 하지만 memcmp를 사용하면 빠른 비교 작업이 도움이 될 것입니다. – brickner

+1

"O (n)보다 좋음"에는 몇 가지주의 사항이 필요합니다. * * 두 문자열의 길이가 관련되어 대상 문자열 길이가 검색되는 문자열의 길이에 비례하므로 * 및 * 입력은 최상의 경우로 제한되고 * 다음은 상수 시간입니다. 최악의 경우는 O (n)입니다. 순진한 검색의 O (n * m) 최악의 경우보다 낫습니다. –

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

+1을 투표했지만 작동하지 않을 것이라고 생각합니다. ('계속'은 'for'루프에 적용되며 'while'에 대해 필요합니다.) –

+0

나는 그것을 두려워했다. ... 수정을 해킹 할 수 있는지 보자. –

+0

그냥 자세히 : bigArrayCount가 사용되지 않는다. –

0

여기에 해결책이 있습니다. 그것은 2로 나뉩니다. 첫 번째 부분은 주로 잠재적 시작을 찾습니다.이 하나를 발견하면 그것, 그것은 잘 알려진 알고리즘 이론에

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

여러 컴파일 오류가 있습니다. 가능성이 가장 높은 것으로 보이는 것으로 수정되었을 때 배열 범위를 벗어나는 버그가 있습니다. –

+0

예, 거기에 약간의 컴파일 오류가 있습니다. 너트는 "코드 블라인드"여야합니다. a-o-b를 볼 수 없습니다. –

+0

'smallArray [first - bigArrayOffset]'은 어떻습니까? 단순화를 위해 big 배열 전체를 검색하여 'bigArrayOffset'이 0이라고 가정합니다. 따라서 짧은 배열이 큰 배열에서 곧 충분히 발생하지 않으면 '첫 번째'가 분명히 너무 커질 것 인 'smallArray [처음]'입니다. –

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

목록 startsbigArraysmallArray의 잠재적 시작 오프셋을 추적하는 데 사용됩니다. smallArray (목록을 최적화하고 메모리 재 할당 횟수를 줄이기 위해 사전에 계산 될 수 있음)에 smallArray[0]의 발생 횟수보다 많은 요소를 포함하지 않습니다. bigArraysmallArray이 포함되도록 충분한 바이트가 남아 있지 않으면 시도하지 않고 smallArray을 찾으면 알고리즘이 중지됩니다. bigArray의 끝에 도달하면 중지됩니다. 그러므로 최악의 경우 실행 시간은 O (1)이고 메모리 사용량은 O (1)이됩니다.

안전하지 않은 코드의 포인터를 사용하고 사전에 계산할 수있는 크기의 고정 배열로 목록을 교체하는 등의 최적화가 가능합니다. 그러나 목록에서 잘못된 오프셋이 제거되고 (작은 내부 루프) 배열에서 잘못된 오프셋을 건너 뛰고 (고정 크기 내부 루프이지만 더 빠른 요소 액세스) 가능한 경우 어느 것이 더 빠른 지 벤치 마크해야합니다.

smallArray의 크기가 큰지 여부에 관계없이 중요합니다. 그렇게하면 while 루프에 starts.Length != 0 || offset <= (bigArrayOffset + bigArrayCount - smallArray.Length)을 검사하는 검사를 추가 할 수 있습니다. 그렇지 않으면 루프가 중지되고 어커런스가 발견되지 않습니다.