喵星球上的点名——记一个用广义SAM根号维护多串的技巧

喵星球的上的点名

给定一个字符串集合S,每次给定T询问S中有多少个字符串中包含T,最后询问S中的每一个字符串包含了多少次给定的T。

思路

考虑将这S个字符串建立广义SAM,那么我们每次将T放到广义SAM中去匹配,最后匹配到的节点的parent子树中来自不同串的结束位置数就是第一问的答案。

同样我们每次匹配完之后在节点处打上标记,第二问S中每一个字符串的答案就是每一个结束位置能够一直往上跳链跳到的不同的节点的标记之和。

考虑如何实现上述过程,在广义SAM中,假设一个节点在SAM中的有效节点数量为\(S_i\),那么可以证明\(\sum_{i=1}^{n}S_i\leq n\sqrt{2n}\),于是对于第一问我们只需要对于每一个结束位置暴力标记parent树上的节点,第二问每一个结束位置去暴力统计parent树上的节点即可。

/*=======================================
 * Author : ylsoi
 * Time : 2019.7.2
 * Problem : luogu2336
 * E-mail : [email protected]
 * ====================================*/
#include<bits/stdc++.h>

#define REP(i,a,b) for(int i=a,i##_end_=b;i<=i##_end_;++i)
#define DREP(i,a,b) for(int i=a,i##_end_=b;i>=i##_end_;--i)
#define debug(x) cout<<#x<<"="<<x<<" "
#define fi first
#define se second
#define mk make_pair
#define pb push_back
typedef long long ll;

using namespace std;

void File(){
    freopen("luogu2336.in","r",stdin);
    freopen("luogu2336.out","w",stdout);
}

template<typename T>void read(T &_){
    _=0; T f=1; char c=getchar();
    for(;!isdigit(c);c=getchar())if(c=='-')f=-1;
    for(;isdigit(c);c=getchar())_=(_<<1)+(_<<3)+(c^'0');
    _*=f;
}

string proc(){
    ifstream f("/proc/self/status");
    return string(istreambuf_iterator<char>(f),istreambuf_iterator<char>());
}

const int maxn=2e5+10;
int n,m,name[maxn],len1[maxn],len2[maxn];

unordered_map<int,int>ch[maxn<<1];
int fa[maxn<<1],len[maxn<<1],cnt=1,last=1;
int vis[maxn<<1],sz[maxn<<1],fg[maxn<<1];

void insert(int x){
    int p=last,np=last=++cnt;
    len[np]=len[p]+1;
    while(p && !ch[p][x])ch[p][x]=np,p=fa[p];
    if(!p)fa[np]=1;
    else{
        int q=ch[p][x];
        if(len[q]==len[p]+1)fa[np]=q;
        else{
            int nq=++cnt;
            ch[nq]=ch[q],len[nq]=len[p]+1,fa[nq]=fa[q];
            fa[q]=fa[np]=nq;
            while(p && ch[p][x]==q)ch[p][x]=nq,p=fa[p];
        }
    }
}

void update(int p,int id){
    for(;p && vis[p]!=id;p=fa[p])
        vis[p]=id,++sz[p];
}

int query(int p,int id){
    int ret=0;
    for(;p && vis[p]!=id;p=fa[p])
        vis[p]=id,ret+=fg[p];
    return ret;
}

int main(){
    File();

    read(n),read(m);

    int now=0;
    REP(i,1,n){
        read(len1[i]),last=1;
        REP(j,1,len1[i])read(name[++now]),insert(name[now]);
        read(len2[i]),last=1;
        REP(j,1,len2[i])read(name[++now]),insert(name[now]);
    }

    now=0;
    REP(i,1,n){
        int o=1;
        REP(j,1,len1[i])update(o=ch[o][name[++now]],i);
        o=1;
        REP(j,1,len2[i])update(o=ch[o][name[++now]],i);
    }

    REP(i,1,m){
        int len0,x,o=1;
        read(len0);
        REP(j,1,len0)read(x),o=ch[o][x];
        printf("%d\n",sz[o]);
        ++fg[o];
    }

    memset(vis,0,sizeof(vis));

    now=0;
    REP(i,1,n){
        int o=1,ans=0;
        REP(j,1,len1[i])ans+=query(o=ch[o][name[++now]],i);
        o=1;
        REP(j,1,len2[i])ans+=query(o=ch[o][name[++now]],i);
        printf("%d ",ans);
    }

    return 0;
}

原文地址:https://www.cnblogs.com/ylsoi/p/11122925.html

时间: 2024-07-31 19:08:58

喵星球上的点名——记一个用广义SAM根号维护多串的技巧的相关文章

「SCOI2012」喵星球上的点名

「SCOI2012」喵星球上的点名 填一个很久以前用 \(\texttt{AC}\) 自动机没填上的坑. 关于本题,能够通过本题的算法很多,这里作者采用的是后缀数组+树状数组的做法. 首先有一个显然的结论:若 \(s_2\) 是 \(s_1\) 的子串,则 \(s_1\) 一定存在一个后缀与 \(s_2\) 的最长公共前缀为 \(|s_2|\). 我们将读入的姓.名.询问串连成一个整体,形成一个字符串 \(s\),且在每一个姓.名.询问串中插入一个不存在文本中的字符,且保证询问串后插入的比姓名串

BZOJ 2754 【SCOI2012】 喵星球上的点名

题目链接:喵星球上的点名 首先可以发现姓和名两个串就是逗你玩的.在两个串中间插入一个\(10001\),当成一个串做就可以了. 于是我们的问题转化为了: 有\(n\)个串\(A_1,A_2,\dots,A_n\)和\(m\)个串\(B_1,B_2,\dots,B_m\),要对于每个\(B_i\)求出它被多少个\(A\)串包含,并要对每个\(A_i\)求出它包含了多少个\(B\)串. 我们先把所有串丢到一个\(AC\)自动机里面,然后构出\(fail\)树.我们知道,如果\(S\)串包含了\(A\

BZOJ_2754__[SCOI2012]_喵星球上的点名_(暴力+后缀数组)

描述 http://www.lydsy.com/JudgeOnline/problem.php?id=2754 给出n个姓名串和m个点名串.求每个点名串在多少人的姓名中出现过(在名中出现或在姓中出现,不能跨越),以及最后每个人被点到多少次. 分析 这种解法是用后缀数组优化一下暴力,(优化了吗?)复杂度并不能保证,然而能A... 我们先把所有名,姓,点名串都接在一个串里面. 然后匹配问题就转化成了后缀的最长公共前缀问题. 但是由于我们讨论问题的对象是名,姓,以及点名串,所以每个部分我们用不同的分隔

BZOJ 2754: [SCOI2012]喵星球上的点名 [后缀数组+暴力]

2754: [SCOI2012]喵星球上的点名 Time Limit: 20 Sec  Memory Limit: 128 MBSubmit: 1906  Solved: 839[Submit][Status][Discuss] Description a180285幸运地被选做了地球到喵星球的留学生.他发现喵星人在上课前的点名现象非常有趣.   假设课堂上有N个喵星人,每个喵星人的名字由姓和名构成.喵星球上的老师会选择M个串来点名,每次读出一个串的时候,如果这个串是一个喵星人的姓或名的子串,那

BZOJ 2754([SCOI2012]喵星球上的点名-后缀数组统计序列集合中子序列出现次数)

2754: [SCOI2012]喵星球上的点名 Time Limit: 20 Sec  Memory Limit: 128 MB Submit: 805  Solved: 380 [Submit][Status][Discuss] Description a180285幸运地被选做了地球到喵星球的留学生.他发现喵星人在上课前的点名现象非常有趣.   假设课堂上有N个喵星人,每个喵星人的名字由姓和名构成.喵星球上的老师会选择M个串来点名,每次读出一个串的时候,如果这个串是一个喵星人的姓或名的子串,

BZOJ 2754: [SCOI2012]喵星球上的点名

2754: [SCOI2012]喵星球上的点名 Time Limit: 20 Sec  Memory Limit: 128 MBSubmit: 1926  Solved: 850[Submit][Status][Discuss] Description a180285幸运地被选做了地球到喵星球的留学生.他发现喵星人在上课前的点名现象非常有趣. 假设课堂上有N个喵星人,每个喵星人的名字由姓和名构成.喵星球上的老师会选择M个串来点名,每次读出一个串的时候,如果这个串是一个喵星人的姓或名的子串,那么这

bzoj2754【SCOI2012】喵星球上的点名

2754: [SCOI2012]喵星球上的点名 Time Limit: 20 Sec  Memory Limit: 128 MB Submit: 1577  Solved: 710 [Submit][Status][Discuss] Description a180285幸运地被选做了地球到喵星球的留学生.他发现喵星人在上课前的点名现象非常有趣.   假设课堂上有N个喵星人,每个喵星人的名字由姓和名构成.喵星球上的老师会选择M个串来点名,每次读出一个串的时候,如果这个串是一个喵星人的姓或名的子串

bzoj 2754 [SCOI2012]喵星球上的点名(后缀数组)

2754: [SCOI2012]喵星球上的点名 Time Limit: 20 Sec  Memory Limit: 128 MBSubmit: 1359  Solved: 618[Submit][Status][Discuss] Description a180285幸运地被选做了地球到喵星球的留学生.他发现喵星人在上课前的点名现象非常有趣.   假设课堂上有N个喵星人,每个喵星人的名字由姓和名构成.喵星球上的老师会选择M个串来点名,每次读出一个串的时候,如果这个串是一个喵星人的姓或名的子串,那

bzoj2754 [SCOI2012]喵星球上的点名 (后缀数组+树状数组)

2754: [SCOI2012]喵星球上的点名 Time Limit: 20 Sec Memory Limit: 128 MB Submit: 2745 Solved: 1190 [Submit][Status][Discuss] Description a180285幸运地被选做了地球到喵星球的留学生.他发现喵星人在上课前的点名现象非常有趣. 假设课堂上有N个喵星人,每个喵星人的名字由姓和名构成.喵星球上的老师会选择M个串来点名,每次读出一个串的时候,如果这个串是一个喵星人的姓或名的子串,那么