来介绍一些基本操作

首先,介绍一下 Suffix Automaton

后缀自动机大概由两部分组成—— DAWG 和 Parent Tree

1.DAWG

DAWG 的中文名字叫做“单词的有向无环图”

它由一个初始节点 init ,若干条转移边,若干个节点组成

DAWG 表示的是状态的转移关系,我们可以记一个点能识别的终止位置集合为 $end-pos(i)$,每个点的子串是一个前缀的一些后缀,这些后缀的长度都在 [minlen,maxlen] 这个区间里

2.Parent Tree

Parent Tree 类似 AC 自动机的 fail 树,是由 $end-pos$ 集合的包含关系构成的一棵树,满足 fa[i] 的 maxlen + 1 等于 i 的 minlen

由这个我们可以知道对 DAWG 拓扑排序相当于对 maxlen 数组快速排序/基数排序,由这个我们也可以知道其实并不用记录每个点的 minlen

Parent Tree 是反串的后缀树

由此我们可以做题

 

bzoj3879 SvT

给你一个串和若干组询问,每组询问包括若干个后缀,你要求出这些后缀两两间最长公共前缀长度的和

sol:后缀 i 和后缀 j 的 lcp 相当于后缀树上 i 的位置和 j 的位置的 LCA 深度

我们把串反过来,然后建 SAM

然后我们虚树 + 树形 dp 就可以了

#include<bits/stdc++.h>
#define LL long long
using namespace std;
inline int read()
{
    int x = 0,f = 1;char ch = getchar();
    for(;!isdigit(ch);ch = getchar())if(ch == '-')f = -f;
    for(;isdigit(ch);ch = getchar())x = 10 * x + ch - '0';
    return x * f;
}
const int maxn = 1200010;
const LL mod = 23333333333333333LL;
int n,a[maxn],pos[maxn],rnk[maxn];
int tr[maxn][26];
int fa[maxn],len[maxn],dfn,root,last;
char s[maxn];
void extend(int c)
{
    int p = last,np = last = ++dfn;
    len[np] = len[p] + 1;
    while(p && !tr[p][c])tr[p][c] = np,p = fa[p];
    if(!p)fa[np] = root;
    else
    {
        int q = tr[p][c];
        if(len[q] == len[p] + 1)fa[np] = q;
        else
        {
            int nq = ++dfn;
            len[nq] = len[p] + 1;memcpy(tr[nq],tr[q],sizeof(tr[nq]));fa[nq] = fa[q],fa[np] = fa[q] = nq;
            while(p && tr[p][c] == q)tr[p][c] = nq,p = fa[p];
        }
    }
}
int first[maxn],to[maxn],nx[maxn],cnt;
LL ans;
int val[maxn];
inline void add(int u,int v){to[++cnt] = v;nx[cnt] = first[u];first[u] = cnt;}
inline void ins(int u,int v){add(u,v);add(v,u);}
int size[maxn],dep[maxn],ff[maxn],bl[maxn],ind[maxn],_tim;
inline void dfs1(int x)
{
    size[x] = 1;ind[x] = ++_tim;
    for(int i=first[x];i;i=nx[i])
    {
        if(to[i] == ff[x])continue;
        ff[to[i]] = x;
        dep[to[i]] = dep[x] + 1;
        dfs1(to[i]);
        size[x] += size[to[i]];
    }
}
inline void dfs2(int x,int col)
{
    bl[x] = col;
    int k = 0;
    for(int i=first[x];i;i=nx[i])
        if(to[i] != ff[x] && size[to[i]] > size[k])k = to[i];
    if(!k)return;
    dfs2(k,col);
    for(int i=first[x];i;i=nx[i])
        if(to[i] != ff[x] && to[i] != k)dfs2(to[i],to[i]);
}
inline int lca(int x,int y)
{
    while(bl[x] != bl[y])
    {
        if(dep[bl[x]] < dep[bl[y]])swap(x,y);
        x = ff[bl[x]];
    }return dep[y] < dep[x] ? y : x;
}
inline bool cmp(const int &x,const int &y){return ind[x] < ind[y];}
int stk[maxn],f[maxn];
inline void dp(int x)
{
    f[x] = val[x] ? 1 : 0;
    for(int i=first[x];i;i=nx[i])
    {
        dp(to[i]);
        ans += (LL)f[x] * f[to[i]] * len[x];
        f[x] += f[to[i]];
    }
    first[x] = 0;
    //cout<<x<<endl;
}
int main()
{
#ifdef Ez3real
    freopen("ww.in","r",stdin);
#endif
    root = last = ++dfn;
    n = read();int q = read();scanf("%s",s + 1);
    reverse(s + 1,s + n + 1);
    for(int i=1;i<=n;i++)extend(s[i] - 'a'),pos[n - i + 1] = last;
    for(int i=1;i<=dfn;i++)add(fa[i],i);dfs1(root);dfs2(root,root);
    memset(first,0,sizeof(first));
    while(q--)
    {
        cnt = 0;
        int k = read();//cout<<k<<"!!"<<endl;
        for(int i=1;i<=k;i++)a[i] = pos[read()];
        sort(a + 1,a + k + 1,cmp);
        int nn = 0;a[++nn] = a[1];
        for(int i=2;i<=k;i++)
            if(a[i] != a[i - 1])a[++nn] = a[i];
        for(int i=1;i<=nn;i++)val[a[i]] = 1;
        int top = 0;
        for(int i=1;i<=nn;i++)
        {
            if(!top){stk[++top] = a[i];continue;}
            int x = a[i],l = lca(x,stk[top]);
            while(ind[l] < ind[stk[top]])
            {
                if(ind[l] >= ind[stk[top - 1]])
                {
                    add(l,stk[top--]);
                    if(l != stk[top])stk[++top] = l;
                    break;
                }else add(stk[top - 1],stk[top]),top--;
            }
            stk[++top] = x;
        }
        while(top > 1)add(stk[top - 1],stk[top]),top--;
        ans = 0;
        dp(stk[1]);
        printf("%lld\n",ans);
        for(int i=1;i<=nn;i++)val[a[i]] = 0;
    }
}
View Code

相关文章:

  • 2019-03-14
  • 2022-01-20
猜你喜欢
  • 2021-09-30
  • 2021-08-08
  • 2022-12-23
  • 2021-09-26
  • 2021-10-18
  • 2021-05-26
  • 2022-02-16
相关资源
相似解决方案