字典树的一个实际应用

字典树是字符串查找里边比较重要的一个算法,相较于朴素的字符串查找来说,后者为循环遍历,对于每一次询问的时间复杂度为O(n),这样查询次数一多就会超时,对于字典树而言,查找次数的多少并不会影响其时间复杂度O(m),m为字符串长度,这样对于多次字符串查找,字典树往往使用得较多。

字典树的构建

字典树的一个实际应用_第1张图片

 如图所示,字典树初始化根节点,该节点不会存放任何字符,可设置为空,权值为0,之后根据输入的字符存放若干节点,相同合并,不同并行,每个节点除了存放字符外,还存放从根节点到既定节点的字符串出现次数,如图可知"apple"出现了5次

对于现在要说的这道题,就是利用了这个思想。

先放代码:

#include
using namespace std;
const int N = 1e5 + 10;
int son[N][26], cnt[N], idx;
void insert(string str , int v)
{
    int p = 0;
    for (int i = 0; i < str.size(); i ++){
        cnt[p] += v ; 
        int u = str[i] - 'a';
        if (!son[p][u]) son[p][u] = ++ idx;
        p = son[p][u];
    }
    cnt[p] += v ;
}
int query(string str)
{
    int p = 0 , ans = 0 ; 
    for (int i = 0; i < str.size() ; i ++) {
        int u = str[i] - 'a';
        if (!son[p][u]) return ans;
        p = son[p][u];
        if(cnt[p] == 0) return ans ;
        ++ ans ; 
    }
    return ans ;
}
string s[N];
int main() {
    int n;
    cin >> n;
    for(int i = 1 ; i <= n ; i ++){
      cin >> s[i] ;
      insert(s[i] , 1) ; 
    }
    for(int i = 1 ; i <= n ; i ++) {
      insert(s[i] , -1) ;
      cout << query(s[i]) << "\n" ;
      insert(s[i] , 1) ; 
    }
    return 0;
}

这里简单说下两个函数的重要作用:

1. insert的函数,用于向字典树中插入字符串。该函数的参数包括字符串以及一个表示计数增量的整数v。在插入过程中,代码遍历字符串的每个字符,并根据字符的值计算出对应的子节点的索引。如果该子节点不存在,则创建新的子节点并更新父节点指向该子节点的指针。同时,在每个节点上,对计数数组cnt进行增量操作,以记录以该节点结尾的字符串的出现次数。

2. query的函数,用于查询特定字符串的出现次数。函数接受一个字符串作为参数,并根据字典树进行遍历。在遍历过程中,函数计算出对应的子节点的索引,如果该子节点不存在则说明字典树中没有以该字符串开头的子串,返回结果为0。如果子节点存在,则将结果自增,并继续遍历下一个字符。同时,函数检查每个节点的计数值是否为0,如果是,则说明到达了一个不存在的字符串节点,返回之前的结果。

原题链接如下:

https://www.lanqiao.cn/problems/3751/learning/?page=1&first_category_id=1&name=%E4%BE%9D%E4%BE%9D%E7%9A%84

你可能感兴趣的:(算法,数据结构)