算法基础——Trie字符串统计

原题连接ios

题目:数组

维护一个字符串集合,支持两种操做:函数

“I x”向集合中插入一个字符串x;
“Q x”询问一个字符串在集合中出现了多少次。
共有N个操做,输入的字符串总长度不超过 10^5,字符串仅包含小写英文字母。spa

输入格式code

第一行包含整数N,表示操做数。字符串

接下来N行,每行包含一个操做指令,指令为”I x”或”Q x”中的一种。get

输出格式io

对于每一个询问指令”Q x”,都要输出一个整数做为结果,表示x在集合中出现的次数。stream

每一个结果占一行。变量

数据范围

1 ≤ N ≤ 2 ∗ 10^4

输入样例:

5
I abc
Q abc
Q ab
I ab
Q ab

对于代码中一些变量和数组的分析和说明(见解)

int son[N][26];
son[N][26]这个数组表示的是,一个字符串经过26个字母组成,且长度不会超过N,因此开son数组开成[N][26]。

int cnt[N];
cnt[N]这个数组是用来记录在一个位置上被打上了多少次标记,该位置上打上的标记次数等于这个字符串出现的字数。

int idx;
idx 用来记录每个字符的所在位置,方便后面询问(Query)时查找,表示如今最新的可用的下标是多少(和单、双链表中idx的做用相似)。

两个函数的意义,表达的思想,须要注意的是,咱们是怎么把Trie树的理论转换成代码实现的

void Insert(char str[]){
    int p = 0;  /* 0表示根节点,0同时也表示节点为空(但在p = 0这里不是表明为空的意思),每次从根节点开始寻找 */
    for(int i = 0; str[i]; i++){
        int u = str[i] - 'a';   /* 将字母转换成数字,方便以后存入Trie树中 */
        if(!son[p][u]) son[p][u] = ++ idx;  /* 若是字符串中的某一个字符在Trie树中不存在,则建立该字符的节点 */
        p = son[p][u];  /* 此时的p就是str中最后一个字符对应的trie树的位置idx。 */
    }
    cnt[p]++;   /* 在p这个位置上打上标记,每加一次说明这一个字符串出现了一次 */
}

int Query(char str[]){
    int p = 0;
    for (int i = 0; str[i]; i++){
        int u = str[i] - 'a';
        if (!son[p][u]) return 0;
        p = son[p][u];
    }
    return cnt[p];
}

完整AC代码:

#include <iostream>
#include <cstdio>

using namespace std;

const int N = 100010;

int son[N][26], cnt[N], idx;

void Insert(char str[]){
    int p = 0;
    for(int i = 0; str[i]; i++){
        int u = str[i] - 'a';
        if(!son[p][u]) son[p][u] = ++ idx;
        p = son[p][u];
    }
    cnt[p]++;
}

int Query(char str[]){
    int p = 0;
    for (int i = 0; str[i]; i++){
        int u = str[i] - 'a';
        if (!son[p][u]) return 0;
        p = son[p][u];
    }
    return cnt[p];
}

int main(){
    int n;
    scanf("%d", &n);
    char op[2];
    while (n--) {
        char str[N];
        scanf("%s%s", op, str);
        if (op[0] == 'I') Insert(str);
        else printf("%d\n", Query(str));
    }
    return 0;
}
相关文章
相关标签/搜索