어떤 문자열 검색 알고리즘이 실제로 가장 빠릅니까? 경우를 가지고

나는 가장 빠른 문자열 검색 알고리즘 인 얼마 동안 붙어있어 많은 의견을 들었지만 결국 확실하지 않습니다.

어떤 사람들은 가장 빠른 알고리즘이 Boyer-Moore이고 Knuth-Morris-Pratt가 실제로 더 빠르다는 말을 들었습니다.

나는 둘 다에 대한 복잡성을 찾았지만 대부분 동일하게 보인다 O(n+m). 최악의 시나리오에서 Boyer-Moore O(nm)는 O (m + 2 * n)를 가진 Knuth-Morris-Pratt와 비교할 때 복잡함 을 발견했습니다 . 여기서 n은 텍스트 길이이고 m은 패턴 길이입니다.

내가 아는 한 Boyer-Moore는 Galil Rule을 사용한다면 최악의 경우를 가지고 있습니다.

내 질문은 실제로 가장 빠른 문자열 검색 알고리즘입니다 (이 질문에는 Boyer-Moore 및 Knuth-Morris-Pratt뿐만 아니라 가능한 모든 스팅 알고리즘이 포함됩니다).

편집 : 이 답변 으로 인해

내가 정확히 찾고있는 것은 :

텍스트 주어 T와 패턴 P내가 모든 모습 찾아야 P의를 T.

또한 P와 T의 길이는 시작 [1,2 000 000]이며 프로그램은 0.15 초 미만으로 실행되어야합니다.

KMP와 Rabin-Karp 가이 문제에 대해 100 % 점수를 얻는 데 충분하다는 것을 알고 있지만 Boyer-Moore를 구현하고 싶었습니다. 이 유형의 패턴 검색에 가장 적합한 것은 무엇입니까?



답변

수행하려는 검색 종류에 따라 다릅니다. 각 알고리즘은 특정 유형의 검색에 대해 특히 잘 수행되지만 검색 컨텍스트를 명시하지 않았습니다.

검색 유형에 대한 일반적인 생각은 다음과 같습니다.

  • Boyer-Moore : 패턴을 사전 분석하고 오른쪽에서 왼쪽으로 비교하여 작동합니다. 불일치가 발생하면 초기 분석을 사용하여 검색중인 텍스트에서 패턴을 얼마나 멀리 이동할 수 있는지 결정합니다. 긴 검색 패턴에 특히 효과적입니다. 특히 텍스트의 모든 단일 문자를 읽을 필요가 없으므로 하위 선형 일 수 있습니다.

  • Knuth-Morris-Pratt : 또한 패턴을 사전 분석하지만 패턴의 초기 부분에서 이미 일치 한 항목을 다시 사용하여 다시 일치시키지 않아도됩니다. 검색 패턴에 재사용 가능한 하위 패턴이 포함될 가능성이 높기 때문에 알파벳이 작 으면 (예 : DNA 염기)이 기능이 매우 효과적입니다.

  • Aho-Corasick : 많은 전처리 과정이 필요하지만 여러 가지 패턴이 필요합니다. 동일한 검색 패턴을 반복해서 찾고 있다는 것을 알고 있다면 검색마다 한 번이 아니라 한 번만 패턴을 분석해야하기 때문에 다른 검색보다 훨씬 좋습니다.

따라서 CS에서 평소와 같이 전반적인 최고에 대한 명확한 대답은 없습니다 . 오히려 작업에 적합한 도구를 선택하는 것이 문제입니다.

최악의 경우 추론에 대한 또 다른 참고 사항 : 최악의 경우를 만드는 데 필요한 검색 종류를 고려하고 실제 상황과 관련이 있는지 철저히 생각하십시오. 예를 들어, O(mn)Boyer-Moore 알고리즘 의 최악의 복잡성은 검색 패턴과 각각 하나의 문자 만 사용하는 텍스트 ( aaa에서 찾은 것과 같은 것) 에서 비롯됩니다 aaaaaaaaaaaaaaaaaaaaa.


답변

나는이 질문에 대답하기에 약간 늦었지만 Z-Algorithm다른 것보다 훨씬 빠르다고 생각 합니다. 최악의 경우 복잡도는 O (m + n)이며 패턴 / 텍스트의 전처리가 필요하지 않습니다. 다른 알고리즘에 비해 코딩이 매우 쉽습니다.

다음과 같은 방식으로 작동합니다.

예를 들어, string이 S ='abaaba'있습니다. 에 대한 z(i)값 을 찾아야 합니다 i=0 to len(S)-1. 설명에 들어가기 전에 먼저 몇 가지 정의를 내려 놓겠습니다.

z(i)= 아니오 접두사의 문자 중 접두사 S와의 접두사가 일치합니다 s(i).

s(i)= ith의 접미사 S.

s(i)값 은 다음과 같습니다 s = 'abaaba'.

s(0) = 'abaaba' = S
s(1) = 'baaba'
s(2) = 'aaba'
s(3) = 'aba'
s(4) = 'ba'
s(5) = 'a'

z 값은 각각

z(0) = 6 = length(S)
z(1) = 0
z(2) = 1
z(3) = 3
z(4) = 0
z(5) = 1

알고리즘에 대한 자세한 내용은 다음 링크를 참조하십시오.

http://codeforces.com/blog/entry/3107

https://www.youtube.com/watch?v=MFK0WYeVEag

이제 z전처리 오버 헤드없이 모든 값 을 찾는 데 O (N)이 필요합니다. 이 논리를 사용하여 주어진 문자열의 패턴을 어떻게 일치시킬 수 있는지 궁금해 할 것입니다.

예를 들어 봅시다. 패턴 (P) : aba, 문자 (T) : aacbabcabaad.

이것을 P $ T 형식으로 넣으십시오. ( $-패턴이나 텍스트로 표시되지 않는 문자입니다. $잠시 후에 중요 함을 알게 될 것 입니다.)

P$T = aba$aacbabcabaad

우리는 len(P)= 3입니다.

모든 z 값 P$T

z(0) = 16 = len(P$T)
z(1) = 0
z(2) = 1
z(3) = 0
z(4) = 1
z(5) = 1
z(6) = 0
z(7) = 0
z(8) = 2
z(9) = 0
z(10) = 0
z(11) = 3
z(12) = 0
z(13) = 1
Z(14) = 1
Z(15) = 0

이제 어느 z(i)= len(P). Ans = 11.따라서 패턴은 Ans-len(P)-1=에 7있습니다. -1입니다 $문자.

이제 왜 $그런 특별한 성격이 중요합니까? 고려 P = 'aaa'T = 'aaaaaaa'. 특수 문자가 없으면 모두 z(i)증분 값을 갖습니다. 아래 공식을 사용하여 텍스트에서 패턴의 위치를 ​​여전히 찾을 수 있습니다.

조건 : z(i)> = len(P)및 위치 : Ans-len(P). 그러나이 경우의 조건은 약간 까다 롭고 혼란 스럽습니다. 나는 개인적으로 특별한 캐릭터 기술을 선호합니다.


답변

소프트웨어에서 가상 주소 지정 (문자를 문자로 가리키는) 형태로 구현 된 컨텐츠 주소 지정 가능 메모리를 사용하십시오 .

그것은 평균 문자열 매칭 알고리즘에 불필요합니다.

CAM은 최대 약 128 자의 패턴 (ASCII, 유니 코드 인 경우 64)까지 수많은 패턴을 동시에 일치시킬 수 있습니다. 그리고 일치하려는 문자열의 문자 길이 당 한 번의 호출과 최대 패턴 길이의 길이 당 메모리에서 무작위로 읽습니다. 따라서 동시에 최대 90,000,000 개의 패턴을 가진 100,000 개의 문자열을 분석하는 경우 (큰 패턴 수를 저장하는 데 약 128GiB가 필요함) RAM에서 12,800,000 개의 임의 읽기를 수행하므로 1ms에 발생합니다.

가상 주소 지정 방법은 다음과 같습니다.

첫 글자를 나타내는 256 개의 시작 주소로 시작하면이 글자는 다음 글자의 256을 가리 킵니다. 패턴이 존재하지 않으면 저장하지 않습니다.

따라서 문자와 문자를 계속 연결하면 가상 주소 지정을 가리키는 128 개의 가상 주소 지정이있는 것과 같습니다.

그것은 작동하지만 900,000,000 개의 패턴을 동시에 일치시키기 위해서는 마지막으로 추가해야 할 트릭이 하나 있습니다.이 문자 버퍼를 많이 재사용하여 시작하지만 나중에는 흩어져 있다는 사실을 이용하고 있습니다. 256 문자를 모두 할당하는 대신 내용을 나열하면 속도가 매우 느려집니다. 기본적으로 모든 문자 포인터 버퍼에 1 문자 만 사용하기 때문에 용량이 100 배 증가합니다. 탈출’).

가장 가까운 이웃 문자열 일치를 얻으려면 이들 중 많은 것을 병렬로 실행하고 계층 구조로 수집하므로 오류를 편견없이 분산시킵니다. 하나만으로 가장 가까운 이웃에게 가려고하면 나무의 시작쪽으로 편향됩니다.


답변