Skip to content

Commit ce7534f

Browse files
committed
bytes: use Rabin-Karp algorithm for LastIndex
Implement LastIndex using the Rabin-Karp algorithm akin to strings.LastIndex name old time/op new time/op delta LastIndexHard1-8 3.16ms ± 1% 1.44ms ± 0% -54.35% (p=0.008 n=5+5) LastIndexHard2-8 3.17ms ± 1% 1.45ms ± 0% -54.27% (p=0.008 n=5+5) LastIndexHard3-8 3.05ms ± 1% 1.44ms ± 1% -52.58% (p=0.008 n=5+5) Change-Id: Ie8ddd179cd84dfa00e3e4e2327ef932975c88670 Reviewed-on: https://go-review.googlesource.com/c/go/+/166258 Run-TryBot: Tobias Klauser <tobias.klauser@gmail.com> TryBot-Result: Gobot Gobot <gobot@golang.org> Reviewed-by: Ian Lance Taylor <iant@golang.org>
1 parent b4baa8d commit ce7534f

File tree

1 file changed

+43
-4
lines changed

1 file changed

+43
-4
lines changed

src/bytes/bytes.go

Lines changed: 43 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -114,12 +114,34 @@ func indexBytePortable(s []byte, c byte) int {
114114
// LastIndex returns the index of the last instance of sep in s, or -1 if sep is not present in s.
115115
func LastIndex(s, sep []byte) int {
116116
n := len(sep)
117-
if n == 0 {
117+
switch {
118+
case n == 0:
118119
return len(s)
120+
case n == 1:
121+
return LastIndexByte(s, sep[0])
122+
case n == len(s):
123+
if Equal(s, sep) {
124+
return 0
125+
}
126+
return -1
127+
case n > len(s):
128+
return -1
119129
}
120-
c := sep[0]
121-
for i := len(s) - n; i >= 0; i-- {
122-
if s[i] == c && (n == 1 || Equal(s[i:i+n], sep)) {
130+
// Rabin-Karp search from the end of the string
131+
hashss, pow := hashStrRev(sep)
132+
last := len(s) - n
133+
var h uint32
134+
for i := len(s) - 1; i >= last; i-- {
135+
h = h*primeRK + uint32(s[i])
136+
}
137+
if h == hashss && Equal(s[last:], sep) {
138+
return last
139+
}
140+
for i := last - 1; i >= 0; i-- {
141+
h *= primeRK
142+
h += uint32(s[i])
143+
h -= pow * uint32(s[i+n])
144+
if h == hashss && Equal(s[i:i+n], sep) {
123145
return i
124146
}
125147
}
@@ -987,3 +1009,20 @@ func hashStr(sep []byte) (uint32, uint32) {
9871009
}
9881010
return hash, pow
9891011
}
1012+
1013+
// hashStrRev returns the hash of the reverse of sep and the
1014+
// appropriate multiplicative factor for use in Rabin-Karp algorithm.
1015+
func hashStrRev(sep []byte) (uint32, uint32) {
1016+
hash := uint32(0)
1017+
for i := len(sep) - 1; i >= 0; i-- {
1018+
hash = hash*primeRK + uint32(sep[i])
1019+
}
1020+
var pow, sq uint32 = 1, primeRK
1021+
for i := len(sep); i > 0; i >>= 1 {
1022+
if i&1 != 0 {
1023+
pow *= sq
1024+
}
1025+
sq *= sq
1026+
}
1027+
return hash, pow
1028+
}

0 commit comments

Comments
 (0)