CodeForces 547E Mike and Friends AC自动机 主席树

本文介绍了一种使用AC自动机和线段树解决字符串匹配问题的方法。具体包括如何构建AC自动机来高效地匹配多个模式串,在一组文本中统计特定模式串出现的次数,以及如何利用线段树维护区间和进行快速查询。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

题意:

给出\(n\)个字符串\(s_i\)\(q\)个询问:

  • \(l,r,k\)\(\sum\limits_{i=l}^{r}count(i, k)\),其中\(count(i,j)\)表示\(s_j\)作为子串在\(s_i\)中出现的次数

分析:

先不考虑查询中\(l,r\)的限制,考虑该字符串\(s_k\)在一个字符串集合中出现的次数。
先将这个字符串集合插入到一棵Trie数中,并且每经过一个节点就将其对应的\(val\)值加\(1\)
这样\(s_k\)对应节点的\(val\)值就是以\(s_k\)前缀的字符串的个数。
然而这还不够,接着构造出一棵fail树,也就是AC自动机中的fail指针构成的树。
这棵fail树中,父节点是子节点的后缀
因为fail树中\(s_k\)是其子节点的后缀,所以再加上子树节点的\(val\)值。
这样就把问题转化为了求子树节点的权值之和:

利用DFS序将子树转为区间,然后用线段树维护区间和

然后考虑上题中的\(l,r\),可以将线段树可持久化或者离线查询加树状数组维护,而且离线应该是很快的。

#include <cstdio>
#include <cstring>
#include <algorithm>
#include <vector>
#include <map>
#include <set>
#include <queue>
using namespace std;
typedef long long LL;
typedef pair<int, int> PII;
#define PB push_back
#define PII pair<int, int>
#define REP(i, a, b) for(int i = a; i < b; i++)
#define PER(i, a, b) for(int i = b - 1; i >= a; i--)
#define ALL(x) x.begin(), x.end()

const int maxn = 200000 + 10;
const int nlogn = maxn * 40;

//persistant segment tree
int lch[nlogn], rch[nlogn], sum[nlogn];
int tot, root[maxn], p[maxn];

void update(int& rt, int pre, int L, int R, int p, int v = 1) {
    rt = ++tot;
    if(L == R) { sum[rt] = sum[pre] + v; return; }
    int M = (L + R) / 2;
    if(p <= M) { rch[rt] = rch[pre]; update(lch[rt], lch[pre], L, M, p, v); }
    else { lch[rt] = lch[pre]; update(rch[rt], rch[pre], M+1, R, p, v); }
    sum[rt] = sum[lch[rt]] + sum[rch[rt]];
}

int qL, qR;
int query(int rt, int pre, int L, int R) {
    int ans = 0;
    if(qL <= L && R <= qR) return sum[rt] - sum[pre];
    int M = (L + R) / 2;
    if(qL <= M) ans += query(lch[rt], lch[pre], L, M);
    if(qR > M) ans += query(rch[rt], rch[pre], M+1, R);
    return ans;
}

//fail tree
vector<int> G[maxn];
int l[maxn], r[maxn], dfs_clock;

void dfs(int u) {
    l[u] = ++dfs_clock;
    for(int v : G[u]) dfs(v);
    r[u] = dfs_clock;
}

//Trie
int sz;
int ch[maxn][26], f[maxn], pos[maxn], fa[maxn];

void insert(int id, char* s) {
    int u = 0;
    for(int i = 0; s[i]; i++) {
        int c = s[i] - 'a';
        if(!ch[u][c]) {
            ch[u][c] = ++sz;
            memset(ch[sz], 0, sizeof(ch[0]));
        }
        fa[ch[u][c]] = u;
        u = ch[u][c];
    }
    pos[id] = u;
}

void getFail() {
    queue<int> Q;
    REP(c, 0, 26) if(ch[0][c]) {
        Q.push(ch[0][c]);
        G[0].PB(ch[0][c]);
    }
    while(!Q.empty()) {
        int r = Q.front(); Q.pop();
        REP(c, 0, 26) {
            int u = ch[r][c];
            if(!u) ch[r][c] = ch[f[r]][c];
            else {
                f[u] = ch[f[r]][c];
                G[f[u]].PB(u);
                Q.push(u);
            }
        }
    }
}

int n, q;
char s[maxn];

int main() {
    scanf("%d%d", &n, &q);
    REP(i, 0, n) {
        scanf("%s", s);
        insert(i + 1, s);
    }
    getFail();
    dfs(0);
    int cur = 0, s;
    REP(i, 1, n + 1) {
        for(int j = pos[i]; j; j = fa[j]) {
            cur++;
            update(root[cur], root[cur-1], 1, dfs_clock, l[j]);
        }
        p[i] = root[cur];
    }

    while(q--) {
        int x, y, k; scanf("%d%d%d", &x, &y, &k);
        qL = l[pos[k]], qR = r[pos[k]];
        printf("%d\n", query(p[y], p[x-1], 1, dfs_clock));
    }

    return 0;
}

转载于:https://www.cnblogs.com/AOQNRMGYXLMV/p/7994362.html

### Codeforces 887E Problem Solution and Discussion The problem **887E - The Great Game** on Codeforces involves a strategic game between two players who take turns to perform operations under specific rules. To tackle this challenge effectively, understanding both dynamic programming (DP) techniques and bitwise manipulation is crucial. #### Dynamic Programming Approach One effective method to approach this problem utilizes DP with memoization. By defining `dp[i][j]` as the optimal result when starting from state `(i,j)` where `i` represents current position and `j` indicates some status flag related to previous moves: ```cpp #include <bits/stdc++.h> using namespace std; const int MAXN = ...; // Define based on constraints int dp[MAXN][2]; // Function to calculate minimum steps using top-down DP int minSteps(int pos, bool prevMoveType) { if (pos >= N) return 0; if (dp[pos][prevMoveType] != -1) return dp[pos][prevMoveType]; int res = INT_MAX; // Try all possible next positions and update 'res' for (...) { /* Logic here */ } dp[pos][prevMoveType] = res; return res; } ``` This code snippet outlines how one might structure a solution involving recursive calls combined with caching results through an array named `dp`. #### Bitwise Operations Insight Another critical aspect lies within efficiently handling large integers via bitwise operators instead of arithmetic ones whenever applicable. This optimization can significantly reduce computation time especially given tight limits often found in competitive coding challenges like those hosted by platforms such as Codeforces[^1]. For detailed discussions about similar problems or more insights into solving strategies specifically tailored towards contest preparation, visiting forums dedicated to algorithmic contests would be beneficial. Websites associated directly with Codeforces offer rich resources including editorials written after each round which provide comprehensive explanations alongside alternative approaches taken by successful contestants during live events. --related questions-- 1. What are common pitfalls encountered while implementing dynamic programming solutions? 2. How does bit manipulation improve performance in algorithms dealing with integer values? 3. Can you recommend any online communities focused on discussing competitive programming tactics? 4. Are there particular patterns that frequently appear across different levels of difficulty within Codeforces contests?
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值