OLD | NEW |
1 // Copyright 2016 The Chromium Authors. All rights reserved. | 1 // Copyright 2016 The Chromium Authors. All rights reserved. |
2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 /** |
| 5 * @unrestricted |
| 6 */ |
| 7 WebInspector.Trie = class { |
| 8 constructor() { |
| 9 this.clear(); |
| 10 } |
4 | 11 |
5 /** | 12 /** |
6 * @constructor | 13 * @param {string} word |
7 */ | 14 */ |
8 WebInspector.Trie = function() | 15 add(word) { |
9 { | 16 var node = this._root; |
10 this.clear(); | 17 ++this._wordsInSubtree[this._root]; |
| 18 for (var i = 0; i < word.length; ++i) { |
| 19 var edge = word[i]; |
| 20 var next = this._edges[node][edge]; |
| 21 if (!next) { |
| 22 if (this._freeNodes.length) { |
| 23 // No need to reset any fields since they were properly cleaned up in
remove(). |
| 24 next = this._freeNodes.pop(); |
| 25 } else { |
| 26 next = this._size++; |
| 27 this._isWord.push(false); |
| 28 this._wordsInSubtree.push(0); |
| 29 this._edges.push({__proto__: null}); |
| 30 } |
| 31 this._edges[node][edge] = next; |
| 32 } |
| 33 ++this._wordsInSubtree[next]; |
| 34 node = next; |
| 35 } |
| 36 this._isWord[node] = true; |
| 37 } |
| 38 |
| 39 /** |
| 40 * @param {string} word |
| 41 * @return {boolean} |
| 42 */ |
| 43 remove(word) { |
| 44 if (!this.has(word)) |
| 45 return false; |
| 46 var node = this._root; |
| 47 --this._wordsInSubtree[this._root]; |
| 48 for (var i = 0; i < word.length; ++i) { |
| 49 var edge = word[i]; |
| 50 var next = this._edges[node][edge]; |
| 51 if (!--this._wordsInSubtree[next]) { |
| 52 delete this._edges[node][edge]; |
| 53 this._freeNodes.push(next); |
| 54 } |
| 55 node = next; |
| 56 } |
| 57 this._isWord[node] = false; |
| 58 return true; |
| 59 } |
| 60 |
| 61 /** |
| 62 * @param {string} word |
| 63 * @return {boolean} |
| 64 */ |
| 65 has(word) { |
| 66 var node = this._root; |
| 67 for (var i = 0; i < word.length; ++i) { |
| 68 node = this._edges[node][word[i]]; |
| 69 if (!node) |
| 70 return false; |
| 71 } |
| 72 return this._isWord[node]; |
| 73 } |
| 74 |
| 75 /** |
| 76 * @param {string=} prefix |
| 77 * @return {!Array<string>} |
| 78 */ |
| 79 words(prefix) { |
| 80 prefix = prefix || ''; |
| 81 var node = this._root; |
| 82 for (var i = 0; i < prefix.length; ++i) { |
| 83 node = this._edges[node][prefix[i]]; |
| 84 if (!node) |
| 85 return []; |
| 86 } |
| 87 var results = []; |
| 88 this._dfs(node, prefix, results); |
| 89 return results; |
| 90 } |
| 91 |
| 92 /** |
| 93 * @param {number} node |
| 94 * @param {string} prefix |
| 95 * @param {!Array<string>} results |
| 96 */ |
| 97 _dfs(node, prefix, results) { |
| 98 if (this._isWord[node]) |
| 99 results.push(prefix); |
| 100 var edges = this._edges[node]; |
| 101 for (var edge in edges) |
| 102 this._dfs(edges[edge], prefix + edge, results); |
| 103 } |
| 104 |
| 105 /** |
| 106 * @param {string} word |
| 107 * @param {boolean} fullWordOnly |
| 108 * @return {string} |
| 109 */ |
| 110 longestPrefix(word, fullWordOnly) { |
| 111 var node = this._root; |
| 112 var wordIndex = 0; |
| 113 for (var i = 0; i < word.length; ++i) { |
| 114 node = this._edges[node][word[i]]; |
| 115 if (!node) |
| 116 break; |
| 117 if (!fullWordOnly || this._isWord[node]) |
| 118 wordIndex = i + 1; |
| 119 } |
| 120 return word.substring(0, wordIndex); |
| 121 } |
| 122 |
| 123 clear() { |
| 124 this._size = 1; |
| 125 this._root = 0; |
| 126 /** @type {!Array<!Object<string, number>>} */ |
| 127 this._edges = [{__proto__: null}]; |
| 128 /** @type {!Array<boolean>} */ |
| 129 this._isWord = [false]; |
| 130 /** @type {!Array<number>} */ |
| 131 this._wordsInSubtree = [0]; |
| 132 /** @type {!Array<number>} */ |
| 133 this._freeNodes = []; |
| 134 } |
11 }; | 135 }; |
12 | |
13 WebInspector.Trie.prototype = { | |
14 /** | |
15 * @param {string} word | |
16 */ | |
17 add: function(word) | |
18 { | |
19 var node = this._root; | |
20 ++this._wordsInSubtree[this._root]; | |
21 for (var i = 0; i < word.length; ++i) { | |
22 var edge = word[i]; | |
23 var next = this._edges[node][edge]; | |
24 if (!next) { | |
25 if (this._freeNodes.length) { | |
26 // No need to reset any fields since they were properly clea
ned up in remove(). | |
27 next = this._freeNodes.pop(); | |
28 } else { | |
29 next = this._size++; | |
30 this._isWord.push(false); | |
31 this._wordsInSubtree.push(0); | |
32 this._edges.push({ __proto__: null }); | |
33 } | |
34 this._edges[node][edge] = next; | |
35 } | |
36 ++this._wordsInSubtree[next]; | |
37 node = next; | |
38 } | |
39 this._isWord[node] = true; | |
40 }, | |
41 | |
42 /** | |
43 * @param {string} word | |
44 * @return {boolean} | |
45 */ | |
46 remove: function(word) | |
47 { | |
48 if (!this.has(word)) | |
49 return false; | |
50 var node = this._root; | |
51 --this._wordsInSubtree[this._root]; | |
52 for (var i = 0; i < word.length; ++i) { | |
53 var edge = word[i]; | |
54 var next = this._edges[node][edge]; | |
55 if (!--this._wordsInSubtree[next]) { | |
56 delete this._edges[node][edge]; | |
57 this._freeNodes.push(next); | |
58 } | |
59 node = next; | |
60 } | |
61 this._isWord[node] = false; | |
62 return true; | |
63 }, | |
64 | |
65 /** | |
66 * @param {string} word | |
67 * @return {boolean} | |
68 */ | |
69 has: function(word) | |
70 { | |
71 var node = this._root; | |
72 for (var i = 0; i < word.length; ++i) { | |
73 node = this._edges[node][word[i]]; | |
74 if (!node) | |
75 return false; | |
76 } | |
77 return this._isWord[node]; | |
78 }, | |
79 | |
80 /** | |
81 * @param {string=} prefix | |
82 * @return {!Array<string>} | |
83 */ | |
84 words: function(prefix) | |
85 { | |
86 prefix = prefix || ""; | |
87 var node = this._root; | |
88 for (var i = 0; i < prefix.length; ++i) { | |
89 node = this._edges[node][prefix[i]]; | |
90 if (!node) | |
91 return []; | |
92 } | |
93 var results = []; | |
94 this._dfs(node, prefix, results); | |
95 return results; | |
96 }, | |
97 | |
98 /** | |
99 * @param {number} node | |
100 * @param {string} prefix | |
101 * @param {!Array<string>} results | |
102 */ | |
103 _dfs: function(node, prefix, results) | |
104 { | |
105 if (this._isWord[node]) | |
106 results.push(prefix); | |
107 var edges = this._edges[node]; | |
108 for (var edge in edges) | |
109 this._dfs(edges[edge], prefix + edge, results); | |
110 }, | |
111 | |
112 /** | |
113 * @param {string} word | |
114 * @param {boolean} fullWordOnly | |
115 * @return {string} | |
116 */ | |
117 longestPrefix: function(word, fullWordOnly) | |
118 { | |
119 var node = this._root; | |
120 var wordIndex = 0; | |
121 for (var i = 0; i < word.length; ++i) { | |
122 node = this._edges[node][word[i]]; | |
123 if (!node) | |
124 break; | |
125 if (!fullWordOnly || this._isWord[node]) | |
126 wordIndex = i + 1; | |
127 } | |
128 return word.substring(0, wordIndex); | |
129 }, | |
130 | |
131 clear: function() | |
132 { | |
133 this._size = 1; | |
134 this._root = 0; | |
135 /** @type {!Array<!Object<string, number>>} */ | |
136 this._edges = [{ __proto__: null }]; | |
137 /** @type {!Array<boolean>} */ | |
138 this._isWord = [false]; | |
139 /** @type {!Array<number>} */ | |
140 this._wordsInSubtree = [0]; | |
141 /** @type {!Array<number>} */ | |
142 this._freeNodes = []; | |
143 } | |
144 }; | |
OLD | NEW |