Trie
trie 树又称前缀树、字典树,用于高效存储、查找字符串集合的数据结构:
- 有一个根结点;
- 从前往后一次遍历每个字符,没有该字符就创建子节点;
- 构成一个符合条件的字符串后在末尾打标记。
const N = 100010 // 字符串个数上限
const numOfLetters = 26 // 26 个小写字母
// 画出 trie 树的图示来理解
var (
// 第一维是 trie 节点的索引号,trie[i][alphabetIdx] 存储第 i 个 trie 节点的
// 第 alphabetIdx 个子节点对应的 trie 节点索引号,0 表示该位置不存在子节点
trie [N][numOfLetters]int
count [N]int // 以当前点作为结尾的单词个数
gIdx int // 全局节点编号;0 是 trie 树的根节点
)
func insert(str string) {
currIdx := 0 // 从 0 号节点(根结点)开始查找
for _, char := range str {
alphabetIdx := int(char - 'a')
if trie[currIdx][alphabetIdx] == 0 {
gIdx++
trie[currIdx][alphabetIdx] = gIdx // 开路
}
currIdx = trie[currIdx][alphabetIdx] // 已经存在的路或新开的路
}
count[currIdx]++
}
func query(str string) int {
currIdx := 0
for _, char := range str {
alphabetIdx := int(char - 'a')
if trie[currIdx][alphabetIdx] == 0 {
return 0
}
currIdx = trie[currIdx][alphabetIdx]
}
return count[currIdx]
}
func readLine() []string {
sc.Scan()
return strings.Split(strings.TrimSpace(sc.Text()), " ")
}
var sc *bufio.Scanner
func main() {
sc = bufio.NewScanner(os.Stdin)
bs := make([]byte, 20000*1024)
sc.Buffer(bs, len(bs))
var n int
fmt.Scanf("%d", &n)
for n > 0 {
n--
line := readLine()
switch line[0] {
case "I":
insert(line[1])
case "Q":
fmt.Println(query(line[1]))
}
}
}
References