|
| 1 | +var AhoCorasick = function (keywords) { |
| 2 | + this._buildTables(keywords) |
| 3 | +} |
| 4 | + |
| 5 | +AhoCorasick.prototype._buildTables = function (keywords) { |
| 6 | + var gotoFn = { |
| 7 | + 0: {}, |
| 8 | + } |
| 9 | + var output = {} |
| 10 | + |
| 11 | + var state = 0 |
| 12 | + keywords.forEach(function (word) { |
| 13 | + var curr = 0 |
| 14 | + for (var i = 0; i < word.length; i++) { |
| 15 | + var l = word[i] |
| 16 | + if (gotoFn[curr] && l in gotoFn[curr]) { |
| 17 | + curr = gotoFn[curr][l] |
| 18 | + } else { |
| 19 | + state++ |
| 20 | + gotoFn[curr][l] = state |
| 21 | + gotoFn[state] = {} |
| 22 | + curr = state |
| 23 | + output[state] = [] |
| 24 | + } |
| 25 | + } |
| 26 | + |
| 27 | + output[state].push(word) |
| 28 | + }) |
| 29 | + |
| 30 | + var failure = {} |
| 31 | + var xs = [] |
| 32 | + |
| 33 | + // f(s) = 0 for all states of depth 1 (the ones just from which 0 can directly transition) |
| 34 | + for (var l in gotoFn[0]) { |
| 35 | + var state = gotoFn[0][l] |
| 36 | + failure[state] = 0 |
| 37 | + xs.push(state) |
| 38 | + } |
| 39 | + |
| 40 | + while (xs.length) { |
| 41 | + var r = xs.shift() |
| 42 | + // for each symbol a such that g(r, a) = s |
| 43 | + for (var l in gotoFn[r]) { |
| 44 | + var s = gotoFn[r][l] |
| 45 | + xs.push(s) |
| 46 | + |
| 47 | + // set state = f(r) |
| 48 | + var state = failure[r] |
| 49 | + while (state > 0 && !(l in gotoFn[state])) { |
| 50 | + state = failure[state] |
| 51 | + } |
| 52 | + |
| 53 | + if (l in gotoFn[state]) { |
| 54 | + var fs = gotoFn[state][l] |
| 55 | + failure[s] = fs |
| 56 | + output[s] = output[s].concat(output[fs]) |
| 57 | + } else { |
| 58 | + failure[s] = 0 |
| 59 | + } |
| 60 | + } |
| 61 | + } |
| 62 | + |
| 63 | + this.gotoFn = gotoFn |
| 64 | + this.output = output |
| 65 | + this.failure = failure |
| 66 | +} |
| 67 | + |
| 68 | +AhoCorasick.prototype.search = function (string) { |
| 69 | + var state = 0 |
| 70 | + var results = [] |
| 71 | + for (var i = 0; i < string.length; i++) { |
| 72 | + var l = string[i] |
| 73 | + while (state > 0 && !(l in this.gotoFn[state])) { |
| 74 | + state = this.failure[state] |
| 75 | + } |
| 76 | + if (!(l in this.gotoFn[state])) { |
| 77 | + continue |
| 78 | + } |
| 79 | + |
| 80 | + state = this.gotoFn[state][l] |
| 81 | + |
| 82 | + if (this.output[state].length) { |
| 83 | + var foundStrs = this.output[state] |
| 84 | + results.push([i, foundStrs]) |
| 85 | + } |
| 86 | + } |
| 87 | + |
| 88 | + return results |
| 89 | +} |
0 commit comments