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

Posted ylsoi

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了喵星球上的点名——记一个用广义SAM根号维护多串的技巧相关的知识,希望对你有一定的参考价值。

喵星球的上的点名

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

思路

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

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

考虑如何实现上述过程,在广义SAM中,假设一个节点在SAM中的有效节点数量为\(S_i\),那么可以证明\(\sum_i=1^nS_i\leq n\sqrt2n\),于是对于第一问我们只需要对于每一个结束位置暴力标记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;

以上是关于喵星球上的点名——记一个用广义SAM根号维护多串的技巧的主要内容,如果未能解决你的问题,请参考以下文章

洛谷 P2336 [SCOI2012]喵星球上的点名

luogu P2336 [SCOI2012]喵星球上的点名

[SCOI2012]喵星球上的点名

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

bzoj 2754 [SCOI2012]喵星球上的点名 (AC自动机+map维护Trie树)

洛谷P2336 [SCOI2012]喵星球上的点名(SA+莫队)