Rolling Hash
rsync라는 유틸리티를 알게되서 알아본 알고리즘.
http://en.wikipedia.org/wiki/Rolling_hash
http://courses.csail.mit.edu/6.006/spring11/rec/rec06.pdf
예시를 이용한 정리.
다음 주어진 문자열 String 2를 String 1에서 검색 또는 비교하는 상황이라 하자.
String 1: "abcdeabcdefaaa", String 2: "bcd"
Naive한 방식은 문자열을 3개의 문자열 단위로 문자 하나씩 비교하는 방식이다.
Rolling Hash 알고리즘을 사용해서 이 상황을 풀어보자.
과정 1-1.
먼저 String 2의 문자열 "bcd"의 Hash 값을 구해놓는다.
Pattern = b*7^0 + c*7^1 d*7^2 = 98*1 + 99*7 + 100
String 1의 substring의 Hash value를 구하고 Pattern의 Hash value와 비교하는 방식이 되겠다.
Substring 1: "abc"
Hash value = a*7^0 + b*7^1 + c*7^2 = 97*1 + 98*7 + 99*
이러면 Hash value를 계산하고 비교하면 되기 때문에 문자열들을 비교시 문자 하나씩 비교할 필요가 없어진다.
과정 1-2.
다음 substring 2는 "bcd"와 pattern "bcd"를 비교한다.
이때 Substring 2: "bcd"를 위해 Hash value를 구해야 하는데
Substring 2는 abc 에서 a가 빠지고 bc에 d가 합쳐진 문자열이다.
즉, Substring 1: a*7^0 + b*7^1 + c*7^2 의 기존 식에서 제거되는 a와 추가되는 d 값을 처리하면 해결된다.(이걸 수학으로 표현하면 빼고 나누고 더하고 이리 처리 하면 되보이는데 ... )
이것을 풀어보면
Base: a*7^0 + b*7^1 + c*7^2
Drop a: a*7^0 + b*7^1 + c*7^2 - a*7^0
Divide by 7: b*7^0 + c*7^1
Add d: b*7^0 + c*7^1 + d*7^2
Substring 2 = [ { ( a*7^0 + b*7^1 + c*7^2 ) - a*7^0 } / 7 ] + d*7^2 = b*7^0 + c*7^1 + d*7^2 가 된다.
실제 사례에서, 긴 Pattern이 적용될때 지수가 커져서 Overflow가 발생된느 상황을 막기위해서 큰 소수로 modulo 연산을 해준다.