diff --git a/core/stringx/node.go b/core/stringx/node.go index c11139ba..ff739185 100644 --- a/core/stringx/node.go +++ b/core/stringx/node.go @@ -100,33 +100,6 @@ func (n *node) find(chars []rune) []scope { func (n *node) longestMatch(chars []rune, paths []*node) (uselessLen, matchLen int, nextPaths []*node) { cur := n var longestMatched *node - findMatch := func(path []*node) (*node, int) { - var ( - result *node - start int - ) - for i := len(path) - 1; i >= 0; i-- { - icur := path[i] - var cur *node - for icur.fail != nil { - if icur.fail.end { - cur = icur.fail - break - } - icur = icur.fail - } - if cur != nil { - if result == nil { - result = cur - start = i - result.depth + 1 - } else if curStart := i - cur.depth + 1; curStart < start { - result = cur - start = curStart - } - } - } - return result, start - } for i := len(paths); i < len(chars); i++ { char := chars[i] @@ -141,21 +114,25 @@ func (n *node) longestMatch(chars []rune, paths []*node) (uselessLen, matchLen i if longestMatched != nil { return 0, longestMatched.depth, nil } + if n.end { return 0, n.depth, nil } - // old path pre longest preMatch - preMatch, preStart := findMatch(paths) + // new path match var jump *node + // old path pre longest preMatch + preMatch, preStart := findMatch(paths) icur := cur for icur.fail != nil { jump, ok = icur.fail.children[char] if ok { break } + icur = icur.fail } + switch { case preMatch != nil && jump != nil: if jumpStart := i - jump.depth + 1; preStart < jumpStart { @@ -172,16 +149,48 @@ func (n *node) longestMatch(chars []rune, paths []*node) (uselessLen, matchLen i } } } + // this longest matched node if longestMatched != nil { return 0, longestMatched.depth, nil } + if n.end { return 0, n.depth, nil } + match, start := findMatch(paths) if match != nil { return start, match.depth, nil } + return len(chars), 0, nil } + +func findMatch(path []*node) (*node, int) { + var result *node + var start int + + for i := len(path) - 1; i >= 0; i-- { + icur := path[i] + var cur *node + for icur.fail != nil { + if icur.fail.end { + cur = icur.fail + break + } + icur = icur.fail + } + if cur != nil { + if result == nil { + result = cur + start = i - result.depth + 1 + } else if curStart := i - cur.depth + 1; curStart < start { + result = cur + start = curStart + } + } + } + + return result, start +} diff --git a/core/stringx/replacer.go b/core/stringx/replacer.go index 29c7ded5..f691d2ee 100644 --- a/core/stringx/replacer.go +++ b/core/stringx/replacer.go @@ -33,9 +33,10 @@ func NewReplacer(mapping map[string]string) Replacer { // Replace replaces text with given substitutes. func (r *replacer) Replace(text string) string { var buf strings.Builder + var paths []*node target := []rune(text) cur := r.node - var paths []*node + for len(target) != 0 { uselessLen, matchLen, nextPaths := cur.longestMatch(target, paths) if uselessLen > 0 { @@ -54,5 +55,6 @@ func (r *replacer) Replace(text string) string { paths = nil } } + return buf.String() } diff --git a/core/stringx/replacer_test.go b/core/stringx/replacer_test.go index 0f8953f5..323874f6 100644 --- a/core/stringx/replacer_test.go +++ b/core/stringx/replacer_test.go @@ -211,3 +211,11 @@ func TestFuzzReplacerCase2(t *testing.T) { t.Errorf("result: %s, match: %v", val, keys) } } + +func TestReplacer_ReplaceLongestMatch(t *testing.T) { + replacer := NewReplacer(map[string]string{ + "日本的首都": "东京", + "日本": "本日", + }) + assert.Equal(t, "东京是东京", replacer.Replace("日本的首都是东京")) +}