diff options
| author | Rui Ueyama <ruiu@google.com> | 2014-09-01 17:47:57 +1000 |
|---|---|---|
| committer | Nigel Tao <nigeltao@golang.org> | 2014-09-01 17:47:57 +1000 |
| commit | 553378079522ca9f131c62acf9cc0a583c76164b (patch) | |
| tree | 22e2432ebc84eb592682409b1ff9eb6b0f450b50 /src/pkg/strings/strings.go | |
| parent | 933169febb90b2a1551a09b5761fda75be9d964d (diff) | |
| download | go-553378079522ca9f131c62acf9cc0a583c76164b.tar.xz | |
strings: use Rabin-Karp algorithm for LastIndex.
benchmark old ns/op new ns/op delta
BenchmarkSingleMatch 49443 52275 +5.73%
BenchmarkIndex 28.8 27.4 -4.86%
BenchmarkLastIndex 14.5 14.0 -3.45%
BenchmarkLastIndexHard1 3982782 2309200 -42.02%
BenchmarkLastIndexHard2 3985562 2287715 -42.60%
BenchmarkLastIndexHard3 3555259 2282866 -35.79%
LGTM=josharian, nigeltao
R=golang-codereviews, ality, josharian, bradfitz, dave, nigeltao, gobot, nightlyone
CC=golang-codereviews
https://golang.org/cl/102560043
Diffstat (limited to 'src/pkg/strings/strings.go')
| -rw-r--r-- | src/pkg/strings/strings.go | 60 |
1 files changed, 48 insertions, 12 deletions
diff --git a/src/pkg/strings/strings.go b/src/pkg/strings/strings.go index 5f19695d3f..761f32a068 100644 --- a/src/pkg/strings/strings.go +++ b/src/pkg/strings/strings.go @@ -43,13 +43,29 @@ func explode(s string, n int) []string { // primeRK is the prime base used in Rabin-Karp algorithm. const primeRK = 16777619 -// hashstr returns the hash and the appropriate multiplicative +// hashStr returns the hash and the appropriate multiplicative // factor for use in Rabin-Karp algorithm. -func hashstr(sep string) (uint32, uint32) { +func hashStr(sep string) (uint32, uint32) { hash := uint32(0) for i := 0; i < len(sep); i++ { hash = hash*primeRK + uint32(sep[i]) + } + var pow, sq uint32 = 1, primeRK + for i := len(sep); i > 0; i >>= 1 { + if i&1 != 0 { + pow *= sq + } + sq *= sq + } + return hash, pow +} +// hashStrRev returns the hash of the reverse of sep and the +// appropriate multiplicative factor for use in Rabin-Karp algorithm. +func hashStrRev(sep string) (uint32, uint32) { + hash := uint32(0) + for i := len(sep) - 1; i >= 0; i-- { + hash = hash*primeRK + uint32(sep[i]) } var pow, sq uint32 = 1, primeRK for i := len(sep); i > 0; i >>= 1 { @@ -85,7 +101,8 @@ func Count(s, sep string) int { } return 0 } - hashsep, pow := hashstr(sep) + // Rabin-Karp search + hashsep, pow := hashStr(sep) h := uint32(0) for i := 0; i < len(sep); i++ { h = h*primeRK + uint32(s[i]) @@ -139,8 +156,8 @@ func Index(s, sep string) int { case n > len(s): return -1 } - // Hash sep. - hashsep, pow := hashstr(sep) + // Rabin-Karp search + hashsep, pow := hashStr(sep) var h uint32 for i := 0; i < n; i++ { h = h*primeRK + uint32(s[i]) @@ -163,22 +180,41 @@ func Index(s, sep string) int { // LastIndex returns the index of the last instance of sep in s, or -1 if sep is not present in s. func LastIndex(s, sep string) int { n := len(sep) - if n == 0 { + switch { + case n == 0: return len(s) - } - c := sep[0] - if n == 1 { + case n == 1: // special case worth making fast + c := sep[0] for i := len(s) - 1; i >= 0; i-- { if s[i] == c { return i } } return -1 + case n == len(s): + if sep == s { + return 0 + } + return -1 + case n > len(s): + return -1 + } + // Rabin-Karp search from the end of the string + hashsep, pow := hashStrRev(sep) + last := len(s) - n + var h uint32 + for i := len(s) - 1; i >= last; i-- { + h = h*primeRK + uint32(s[i]) + } + if h == hashsep && s[last:] == sep { + return last } - // n > 1 - for i := len(s) - n; i >= 0; i-- { - if s[i] == c && s[i:i+n] == sep { + for i := last - 1; i >= 0; i-- { + h *= primeRK + h += uint32(s[i]) + h -= pow * uint32(s[i+n]) + if h == hashsep && s[i:i+n] == sep { return i } } |
