首页 > 其他分享 >CF547E Mike and Friends题解

CF547E Mike and Friends题解

时间:2023-06-08 20:15:24浏览次数:39  
标签:ch int 题解 CF547E tot 区间 edge fail Friends

题目链接

温馨提示:做本题之前可以先尝试这个:洛谷 P2414 阿狸的打字机(是简单版的uwu)。

首先,这个题涉及多模式串匹配,首先想 AC 自动机。但是有个问题:我们如何去计算一个串出现的次数呢?

我们先考虑查询一个串 \(a\) 在串 \(b\) 中出现的次数。首先,在 AC 自动机上有一个性质,就是如果从某一点 \(u\) 的 \(fail\) 往回跳的时候,会直接或间接经过另一个串 \(s\) 的结束位置,那这个点上一定有 \(s\) 串。这个结论很显然,因为 \(fail\) 跳的是最长相同后缀,那 \(s\) 一定是以 \(u\) 为结束点的一个串的后缀。我们首先想到一种暴力,那就是在 Trie 树上往下走 \(b\) 串,然后每走到一个点都跳 \(fail\),看 \(a\) 的结束位置能被经过几次。但是这样显然复杂的会炸,更别提区间查了。怎么办呢?

我们又发现一个性质。\(fail\) 每次只会往深度更小的点跳,这意味着,\(fail\) 的指针会形成一个树的结构!没错!我们可以建立一个 \(fail\) 树。那刚才的问题就可以转变为,以 \(a\) 串的结束位置为根的子树上,有多少个节点属于 \(b\) 串。这个可以通过dfn序和树状数组直接维护(想写线段树没人拦你)。每次将 \(b\) 上的节点赋成 \(1\),然后直接查询即可。查完之后,再赋成 \(0\)。(这就是阿狸的打字机的做法awa)

我们来回到这个题。这个题要求区间查。我们首先来想,如果只查一个区间,那和上一道题类似,将整个区间的字符串的每个点都加上出现次数,然后树状数组查。但是这个是一堆区间。这时候,我们有一个常用的套路(在另一个题里也有用到(这道题)。那就是,既然是求区间和,那我们可以利用前缀和的性质,分别求出 \([1, l-1]\) 和 \([1, r]\) 的答案,然后相减即可。

然鹅如果暴力做的话还是会不断加不断删。那我们可以考虑把询问离线下来,把位置按大小排序,这样,可以保证每个字符串只加不减,而且只会加一次。这样,这道题就可以做了。复杂度 \(O(q\log q+\max(q, \lvert s \rvert) \log \lvert s \rvert)\)。

代码:

#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int N = 2e5+100, M = 5e5+100;

//快读
inline int read(){
    int x = 0;char ch = getchar();
    while(ch<'0' || ch>'9'){
        ch = getchar();
    }
    while(ch>='0'&&ch<='9'){
        x = x*10+ch-48;
        ch = getchar();
    }
    return x;
}

//AC自动机
struct Trie{
    int son[26];
}t[N];
int ps[N];
int idx = 1;//因为要建树,所以最好从1开始加点。
string ss[N];//因为后面还要用到原来的字符串,所以这里用string来存一下。
void insert(string s, int id){
    int lth = s.length();
    int u = 1;
    int v ;
    for(int i = 0; i<lth; i++){
        v = s[i]-'a';
        if(!t[u].son[v]){
            t[u].son[v] = ++idx;
        }
        u = t[u].son[v];
    }
    ps[id] = u;
}

queue<int> q;
int fail[N];

struct node{
    int nxt, to;
}edge[N];
int head[N], tot;
void add(int u, int v){
    edge[++tot].nxt = head[u];
    edge[tot].to = v;
    head[u] = tot;
}
void build(){
    int u = 1;
    for(int i = 0; i<26; ++i){
        if(t[u].son[i]){
            q.push(t[u].son[i]);
            fail[t[u].son[i]] = 1;
            add(1, t[u].son[i]);//fail树
        }
    }
    while(!q.empty()){
        u = q.front();
        q.pop();
        for(int i = 0; i<26; i++){
            if(t[u].son[i]){
                q.push(t[u].son[i]);
                int now = fail[u];
                while(now!=1&&!(t[now].son[i])){
                    now = fail[now];
                }
                if(t[now].son[i]){
                    now = t[now].son[i];
                }
                fail[t[u].son[i]] = now;
                add(now, t[u].son[i]);//fail树
            }
        }
    }
}
//fail树
int dfn[N], cntd;
int siz[N];
void dfs_fail(int u){
    siz[u] = 1;
    dfn[u] = ++cntd;
    for(int i = head[u]; i; i = edge[i].nxt){
        int v = edge[i].to;
        dfs_fail(v);
        siz[u]+=siz[v];
    }
}
//树状数组
int tc[N];
inline int lowbit(int x){
    return x&(-x);
}
void ins(int x, int pos){
    for(int i = pos; i<=cntd; i+=lowbit(i)){
        tc[i]+=x;
    }
}
int query(int pos){
    int sum = 0;
    for(int i = pos; i; i-=lowbit(i)){
        sum+=tc[i];
    }
    return sum;
}

char s[N];
int n, Q;
struct question{
    int p, k, id;
    bool lr;
    bool operator < (const question &b) const{
        return p<b.p;
    }
}qu[M<<1];
int totq;
int ans[M];

//添加字符串
void adds(int id){
    int u = 1, lth = ss[id].length();
    for(int i = 0; i<lth; ++i){
        int v = ss[id][i]-'a';
        ins(1, dfn[t[u].son[v]]);
        u = t[u].son[v];
    }
}
int main(){
    n = read(), Q = read();
    for(int i = 1 ;i<=n; i++){
        cin >> ss[i];
        insert(ss[i], i);
    }
    int K, l, r;
    for(int i = 1; i<=Q; ++i){
        l = read(), r = read(), K = read();
        qu[++totq].p = l-1 ,qu[totq].id = i, qu[totq].k = K,qu[totq].lr = 0;
        qu[++totq].p = r, qu[totq].id = i, qu[totq].k = K, qu[totq].lr = 1;
    }
    build();
    dfs_fail(1);
    sort(qu+1, qu+totq+1); 
    int now = 0;
    for(int i = 1; i<=totq; i++){
        while(now<qu[i].p){
            adds(++now);
        }
        int tk = qu[i].k;
        int u = ps[tk];
        if(qu[i].lr){//如果是右端点
            int tmp = query(dfn[u]+siz[u]-1)-query(dfn[u]-1);
            ans[qu[i].id] = tmp-ans[qu[i].id];
        }else{//是左端点
            ans[qu[i].id] = query(dfn[u]+siz[u]-1)-query(dfn[u]-1);
        }
    }
    for(int i = 1; i<=Q; i++){
        printf("%d\n", ans[i]);
    }
    return 0;
}

标签:ch,int,题解,CF547E,tot,区间,edge,fail,Friends
From: https://www.cnblogs.com/frostwood/p/17467517.html

相关文章

  • rsa加解密的内容超长的问题解决
    一.现象:     有一段老代码用来加密的,但是在使用keyA的时候,抛出了异常:javax.crypto.IllegalBlockSizeException:Datamustnotbelongerthan117bytes。老代码已经做了分段的加密,应该是已经考虑了加密长度的问题才对。换了另一个线上代码中的keyB,正常加......
  • 【前端跨域】CORS跨域问题解决思路
    目录一、Nginx跨域配置二、Spring项目跨域配置参考资料一、Nginx跨域配置在Nginx中配置跨域请求,主要可以通过设置HTTP响应头部的方式进行。以下是具体实现步骤:在Nginx的配置文件中找到对应location配置块,例如:server{listen80;server_nameexample.com;......
  • [第五届蓝桥杯省赛C++B组]省赛全题目题解
    文章目录快速分支通道酒精与饮料切面条李白打酒史丰收运算打印图形奇怪的分式六角填数蚂蚁感冒地宫取宝小朋友排队1.题目啤酒和饮料算法标签:枚举题目描述:题目答案:题目思路:题目代码:2.题目切面条来源:第五届蓝桥杯省赛C++B组算法标签递推题目描述:题目答案:题目思路:题目代码:3.题目......
  • [第七届蓝桥杯省赛C++B组]省赛全题目题解
    文章目录快速分支通道煤球数目生日蜡烛凑算式快速排序抽签方格填数剪邮票四平方和交换瓶子最大比例煤球数目题目来源:第七届蓝桥杯省赛C++B组算法标签:递推题目描述:题目答案:题目思路:题目代码生日蜡烛题目来源:第七届蓝桥杯省赛C++B组算法标签:枚举,双指针题目描述:题目答案:题目思路:题目代......
  • Android问题解决:android.util.Base64.encode 导致签名不匹配 SignatureDoesNotMatch
    文章目录前文:遇到问题一问:为什么SignatureDoesNotMatch二问:为什么SignatureDoesNotMatch三问:Signature请求参数为什么多了%0A四问:Signature为什么多了换行五问:android.util.Base64.encode的用法前文:遇到问题在折腾《ESP32-C3入门教程——导读》时,需要对接阿里云物联网平台。想要......
  • P3498 [POI2010]KOR-Beads 题解
    前言:最近在做哈希的题,发现了这道好题,看题解里很多大佬的方法都很巧妙,自己就发一个较为朴素的方法吧。题意:题目传送门给你一个序列,需要求出数k,使划分的子串长度为k时,不同的子串数量最多。还要注意几件事:子串可以反转,比如(1,2,3)看做与(3,2,1)相同。如果不能正好划......
  • P8933 [JRKSJ R7] 技巧性的块速递推 题解
    题目传送门题意:简单来说就是一个涂色游戏。有一个n×m的棋盘需要涂色。每格只能涂黑色或白色两种颜色。横、竖、斜连续3格颜色不能相同。横、竖、斜连续4格颜色不能有3个相同颜色,即只能是2个黑和2个白。最后让你统计出所有符合条件的涂色方式的方......
  • P1751 贪吃虫 题解
    题意:题目传送门在一棵n个结点的树上,有k个贪吃虫去吃食物。每个贪吃虫都走到达食物的唯一路径。当一条贪吃虫通向食物的道路上有另一条贪吃虫,则较远的那只停止移动。多条贪吃虫要进入同一节点时,编号最小的才能进入,其他的停止移动。贪吃虫的移动速度皆为1。一只贪吃虫吃......
  • 【题解】 P2221 [HAOI2012]高速公路
    传送门输入时将\(r\)先减1。发现收费之和为$$ans=\sum\limits_{i=l}^{r}a_i\times(r-l+1)\times(i-l+1)$$化简得\[ans=\sum\limits_{i=l}^{r}a_i\times(-i^2+i\times(r+l)+(r-r\timesl-l+1))\]发现可以用线段树来维护:\(s......
  • Codeforces Round 876 (Div. 2) 题解 A - D
    A.TheGoodArray题目大意给定两个整数\(n\)和\(k\),规定一个\(01\)数列为好的的条件是,对于\(1\simn\)中任意的\(i\),都有:\(a\)的前\(i\)个元素中至少有\(\lceil\frac{i}k\rceil\)个都是\(1\)。\(a\)的后\(i\)个元素中至少有\(\lceil\frac{i}k\rceil\)个都是......