进阶数据结构应用-单词

目录

题目-单词

问题分析

目标是每个单词出现的次数, 分为两类

  • 如果当前单词不是某个单词子串, 那么出现次数是 1 1 1
  • 如果当前单词是某个单词的子串, 如何计算次数?

我们可以统计这样一个数值, 对于串 s s s, 有多少个前缀是以 s s s为后缀的 , 这个值就是答案

对于字符串 t t t, 某一个前缀的后缀 是 s s s, 那么答案需要累计

如何计算?

对于当前串 s s s, 计算当前后缀 和哪些前缀匹配, 在AC自动机直接可以迭代计算

fail[u], fail[fail[u]], fail[fail[fail[u]]]...都是和当前后缀匹配的前缀

递推思想, 对于当前后缀出现次数 假设是 x x x, 将前面的前缀全部累加 + x +x +x

例如某个后缀是aaa, 与之匹配的aa前缀也需要 + 1 +1 +1, 与aa匹配的前缀a也需要 + 1 +1 +1

因此直接按照AC自动机的fail指针拓扑序逆序递推就能得到答案

算法步骤

  • 将读入的字符串加入到Trie中, 假设当前遍历的是节点 u u u, u u u作为后缀出现次数累加cnt[u]++
  • 假设当前添加的是第 k k k个字符串, 当字符串添加完毕后, 记录结尾的Trie指针id[k] = u
  • 构建AC自动机
  • 按照拓扑序逆序从后向前递推 , 因为后面的是更长的后缀 , cnt[fail[i]] += cnt[i]
  • 按照 i d id id计算每个字符串出现的次数

代码实现

cpp 复制代码
#include <bits/stdc++.h>

using namespace std;

const int N = 210, M = 1e6 + 10;

int n;
int tr[M][26], idx;
int f[M];
int q[M], h, t;
int fail[M], id[N];

void insert(string &s, int k) {
    int p = 0;
    for (int i = 0; s[i]; ++i) {
        int c = s[i] - 'a';
        if (!tr[p][c]) tr[p][c] = ++idx;
        p = tr[p][c];
        // 因为p是代表某个后缀, 因此需要在p = tr[p][c]后面f[p]++
        f[p]++;
    }

    id[k] = p;
}

void build() {
    h = 0, t = -1;
    for (int i = 0; i < 26; ++i) {
        if (tr[0][i]) q[++t] = tr[0][i];
    }

    while (h <= t) {
        int u = q[h++];
        for (int i = 0; i < 26; ++i) {
            int v = tr[u][i];
            if (!v) continue;
            int j = fail[u];
            while (j && !tr[j][i]) j = fail[j];
            if (tr[j][i]) j = tr[j][i];
            fail[v] = j;
            q[++t] = v;
        }
    }
}


int main() {
    ios::sync_with_stdio(false);
    cin.tie(0);

    cin >> n;
    string s;
    for (int i = 0; i < n; ++i) {
        cin >> s;
        insert(s, i);
    }

    build();

    for (int i = t; i >= 0; --i) {
        int u = q[i];
        f[fail[u]] += f[u];
    }

    for (int i = 0; i < n; ++i) cout << f[id[i]] << '\n';

    return 0;
}
相关推荐
wabs6668 小时前
关于图论【卡码网104.建造最大岛屿的思考】
数据结构·算法·图论
冻柠檬飞冰走茶9 小时前
PTA基础编程题目集 7-15 计算圆周率(C语言实现)
c语言·开发语言·数据结构·算法
Tisfy12 小时前
LeetCode 3517.最小回文排列 I:排序(Python两行版) / 计数(O(n)时间+O(C)空间+字符串原地修改)
c语言·python·leetcode·字符串·排序·计数排序·回文
冻柠檬飞冰走茶12 小时前
PTA基础编程题目集 7-13 日K蜡烛图(C语言实现)
c语言·开发语言·数据结构·算法
夜不会漫长13 小时前
数据结构:栈和队列(1)
数据结构
青山木14 小时前
Hot 100 --- 电话号码的字母组合
java·数据结构·算法·leetcode·逻辑回归
闪电悠米14 小时前
力扣hot100-240.搜索二维矩阵2-单调性剪枝详解
数据结构·算法·leetcode·矩阵·哈希算法
雨落在了我的手上14 小时前
Java数据结构(九):栈和队列
java·开发语言·数据结构
春生野草15 小时前
个人笔记--大顶堆和基数排序
java·数据结构·算法
paeamecium17 小时前
【PAT甲级真题】- Rational Sum (20)
数据结构·c++·python·算法·pat考试·pat