KMP 또는 Boyer-Moore와 같은 몇 가지 기본 문자열 일치 알고리즘을 알고 있지만 모두 검색하기 전에 패턴을 분석하지만 하나의 문자가 있으면 분석 할 것이 많지 않습니다. 텍스트의 모든 문자를 비교하는 순진한 검색보다 더 나은 알고리즘이 있습니까?
KMP 또는 Boyer-Moore와 같은 몇 가지 기본 문자열 일치 알고리즘을 알고 있지만 모두 검색하기 전에 패턴을 분석하지만 하나의 문자가 있으면 분석 할 것이 많지 않습니다. 텍스트의 모든 문자를 비교하는 순진한 검색보다 더 나은 알고리즘이 있습니까?
답변:
최악의 경우는 O(N)
매우 미세한 미세 최적화 가 있음을 이해합니다 .
순진한 방법은 각 문자에 대한 문자 비교 및 텍스트 끝 비교를 수행합니다.
사용 감시 (텍스트의 끝에서 대상 문자의 예를 복사하는) 문자 당 1 비교의 수를 줄일 수 있습니다.
비트 트위들 링 레벨에는 다음이 있습니다.
#define haszero(v) ( ((v) - 0x01010101UL) & ~(v) & 0x80808080UL )
#define hasvalue(x, n) ( haszero((x) ^ (~0UL / 255 * (n))) )
단어 ( x
)의 바이트 가 특정 값 ( n
)을 갖는지 알기 위해 .
하위 표현식 v - 0x01010101UL
은 해당 바이트 in v
이 0 이상일 때마다 모든 바이트에서 높은 비트 세트로 평가됩니다 0x80
.
하위 표현식 ~v & 0x80808080UL
은 바이트 수가 높은 비트 세트를 v
갖지 않는 바이트 단위로 높은 비트 세트로 평가됩니다 (따라서 바이트는보다 작음 0x80
).
이 두 하위 표현식 ( haszero
) 을 AND 처리 하면 첫 번째 하위 표현식 v
보다 큰 값으로 설정된 상위 비트 0x80
가 두 번째 하위 마스크에 의해 가려 지기 때문에 결과는 바이트 가 0 인 상위 비트 세트입니다 (4 월 27 일, 1987 년 Alan Mycroft).
이제 x
원하는 바이트 값으로 채워진 단어 로 테스트 할 값 ( ) 을 XOR 할 수 있습니다 ( n
). 값 자체를 XOR하면 0 바이트가되고 그렇지 않으면 0이 아니므로 결과를로 전달할 수 있습니다 haszero
.
이것은 종종 전형적인 strchr
구현 에서 사용됩니다 .
(Stephen M Bennet은 2009 년 12 월 13 일에이를 제안했습니다. Bit Twiddling Hacks ).
추신
이 코드의 조합 깨진
1111
A와 다음의 '0
해킹은 무차별 대입 테스트를 통과합니다 (인내심).
#include <iostream>
#include <limits>
bool haszero(std::uint32_t v)
{
return (v - std::uint32_t(0x01010101)) & ~v & std::uint32_t(0x80808080);
}
bool hasvalue(std::uint32_t x, unsigned char n)
{
return haszero(x ^ (~std::uint32_t(0) / 255 * n));
}
bool hasvalue_slow(std::uint32_t x, unsigned char n)
{
for (unsigned i(0); i < 32; i += 8)
if (((x >> i) & 0xFF) == n)
return true;
return false;
}
int main()
{
const std::uint64_t stop(std::numeric_limits<std::uint32_t>::max());
for (unsigned c(0); c < 256; ++c)
{
std::cout << "Testing " << c << std::endl;
for (std::uint64_t w(0); w != stop; ++w)
{
if (w && w % 100000000 == 0)
std::cout << w * 100 / stop << "%\r" << std::flush;
const bool h(hasvalue(w, c));
const bool hs(hasvalue_slow(w, c));
if (h != hs)
std::cerr << "hasvalue(" << w << ',' << c << ") is " << h << '\n';
}
}
return 0;
}
하나의 chararacter = 1 바이트로 가정하는 대답에 대한 많은 공감대가 이제는 표준이 아닙니다.
발언 감사합니다.
대답은 멀티 바이트 / 가변 폭 인코딩에 대한 에세이 이외의 것입니다 :-) (모든 공정성에서 내 전문 분야가 아니며 OP가 찾고있는 것이 확실하지 않습니다).
어쨌든 위의 아이디어 / 틱은 MBE (특히 자체 동기화 인코딩 )에 다소 적용될 수있는 것 같습니다 .
strchr
/에 대한 호출을 포함합니다 strstr
(예 : GNUlib coreutils mbschr )0x01010101UL
에서 한 줄과 ~0UL / 255
다음 줄에 글을 쓰는 이유를 이해하지 못합니다 . 서로 다른 가치를 가져야한다는 인상을줍니다. 그렇지 않으면 왜 두 가지 다른 방식으로 씁니까?
#define
s로 확장 되므로 여러 (8?) 명령이 필요 합니다 ( (((x) ^ (0x01010101UL * (n)))) - 0x01010101UL) & ~((x) ^ (0x01010101UL * (n)))) & 0x80808080UL )
. 싱글 바이트 비교가 더 빠르지 않습니까?
주어진 텍스트에서 단일 문자의 모든 발생을 검색하는 모든 텍스트 검색 알고리즘은 텍스트의 각 문자를 적어도 한 번 읽어야합니다. 이 방법은 일회성 검색에 충분하므로 더 나은 알고리즘을 사용할 수 없습니다 (이 경우에는 "선형"또는 O (N)이라고하는 런타임 순서 측면에서 생각할 때 N은 문자 수임) 검색).
그러나 실제 구현의 경우 실행 시간 순서를 전체적으로 변경하지 않고 실제 실행 시간을 줄이는 미세 최적화가 많이 가능합니다. 그리고 목표가 한 인물의 모든 사건을 찾는 것이 아니라 첫 번째 사건만을 찾는 것이라면 물론 첫 사건에서 멈출 수 있습니다. 그럼에도 불구하고, 그 경우에도 최악의 경우는 여전히 찾고있는 문자가 텍스트의 마지막 문자 이므로이 목표의 최악의 런타임 순서는 여전히 O (N)입니다.
"haystack"을 두 번 이상 검색하면 히스토그램 기반 접근 방식이 매우 빠릅니다. 히스토그램이 작성된 후에는 답을 찾기 위해 포인터 검색 만 필요합니다.
검색된 패턴이 있는지 여부 만 알면 간단한 카운터가 도움이 될 수 있습니다. 각 문자가 건초 더미에서 발견되는 위치 또는 첫 번째 발생 위치를 포함하도록 확장 될 수 있습니다.
string haystack = "agtuhvrth";
array<int, 256> histogram{0};
for(character: haystack)
++histogram[character];
if(histogram['a'])
// a belongs to haystack
이 매우 동일한 문자열에서 문자를 두 번 이상 검색해야하는 경우 문자열을 더 작은 부분으로, 재귀 적으로 나누고, 각 부분에 대해 블룸 필터를 사용하는 것이 가능합니다.
블룸 필터는 캐릭터가 아닌지 확실히 알려줄 수 있기 때문에 필터를 "표현"는 무엇 문자열의 일부에 문자를 검색하는 동안, 당신은 몇 가지 부분을 건너 뛸 수 있습니다.
예를 들어 : 다음 문자열의 경우 4 개의 부분 (각 11 자 길이)으로 분할하고 각 부분에 해당 부분의 문자로 블룸 필터 (아마도 4 바이트)를 채울 수 있습니다.
The quick brown fox jumps over the lazy dog
| | | |
예를 들어 캐릭터에 대한 검색 속도를 높일 수 있습니다 a
. 블룸 필터에 좋은 해시 함수를 사용하면 높은 확률로 첫 번째, 두 번째 또는 세 번째 부분을 모두 검색 할 필요가 없습니다. 따라서 33 문자를 확인하지 않고 16 바이트 만 검사하면됩니다 (4 개의 블룸 필터). 이것은 여전히O(n)
상수 (분수) 계수 유효합니다 (이를 유효하게하려면 검색 문자의 해시 함수 계산 오버 헤드를 최소화하기 위해 더 큰 부분을 선택해야합니다).
재귀 트리 같은 접근 방식을 사용하면 가까이에 있어야합니다 O(log n)
.
The quick brown fox jumps over the lazy dog
| | | | | | | |---|-X-| | (1 Byte)
| | | |---X---|---- (2 Byte)
| |-----X------ (3 Byte)
-------------------------------|-----X------ (4 Byte)
---------------------X---------------------| (5 Byte)
이 구성에서 (다시, 우리가 운이 좋으며 필터 중 하나에서 오 탐지를 얻지 않았다고 가정) 다시 확인해야합니다.
5 + 2*4 + 3 + 2*2 + 2*1 bytes
마지막 부분에 도착하기 위해 (찾을 때까지 3 문자를 확인해야합니다. a
).
좋은 (상기보다 나은) 세분화 체계를 사용하면 꽤 좋은 결과를 얻을 수 있습니다. (참고 : 예에 표시된 것처럼 오 탐지 확률이 낮도록 나무의 루트에있는 블룸 필터가 나뭇잎에 근접한 것보다 커야합니다.)