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


27

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

어떤 사람들은 가장 빠른 알고리즘이 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를 구현하고 싶었습니다. 이 유형의 패턴 검색에 가장 적합한 것은 무엇입니까?


6
선택한 언어로 테스트했을 때 무엇을 찾았습니까?
Walter

4
일부 테스트에서는 Boyer-Moore가 다른 KMP에서 더 좋았지 만, "최고의"구현이 확실하지 않습니다. 선택 언어는 C ++ 태그에 있습니다 ( "선택 언어"를 썼기 때문에 그것을 보았는지 확실하지 않습니다). 추신 : 나는 최고의 테스트를 테스트했는지 확실하지 않습니다.
vandamon taigi


O (m + 2 * n)을 갖는 Knuth-Morris-Pratt ... 당신은 O (m + n)을 의미합니다.
Jules

적절한 알고리즘 복잡성을 가진 하나를 선택한 다음 프로파일 러로 쓰레기를 미세 조정하십시오. 항상 나를 위해 일했습니다. :-D

답변:


38

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

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

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

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

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

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

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


사용할 전체 영어 알파벳이 있고 질문을 업데이트했습니다. 이걸로 시작하지 않아서 죄송합니다.
vandamon taigi

그리고 그래, 나는 그런 검색에도 빠를 필요가 없다
vandamon taigi

1

나는이 질문에 대답하기에 약간 늦었지만 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). 그러나이 경우의 조건은 약간 까다 롭고 혼란 스럽습니다. 나는 개인적으로 특별한 캐릭터 기술을 선호합니다.


1
여기서 직접 설명해 주시겠습니까? 외부 사이트로 연결되는 링크를 사용하여 정교하게 만들 수 있지만 답변의 핵심은 다른 사이트로 연결되는 링크를 따라 가지 않고 답변 자체에 있어야합니다.

z- 알고리즘은 기본적으로 kmp와 동일합니다. 훨씬 빠릅니다.
Thomas Ahle

2
@ThomasAhle에 동의합니다. 컴퓨팅 z 전처리입니다. 그래도 좋은 설명입니다. O(n)이 답변으로 인해 KMP 전처리에서 Z 전처리로 변환 하는 방법을 찾았 습니다 . 여기
leewz

-1

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

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

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

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

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

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

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

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


4
@MagnusRobertCarlWoot는 roucer81과 동일한 gavatar를 가지고 있으며 천문학적으로 해시 코드 충돌이 발생하거나 동일한 이메일 주소를 가지고 있습니다. 두 계정 뒤에 동일한 개인이있는 경우 "문의하기"양식을 사용하여 병합하여이 답변에 대한 찬성으로 얻은 평판에 대한 적절한 신용을 얻도록해야합니다.
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.