luogu SP8093 后缀自动机+树状数组+dfs序

这题解法很多,简单说几个:

1. 线段树合并,时间复杂度是 $O(nlog^2n)$ 的.

2. 暴力跳 $fail,$ 时间复杂度 $O(n\sqrt n),$ 比较暴力.

3. 建立后缀树后在 $dfs$ 序上数点,时间复杂度为 $O(nlogn),$ 十分优秀.

Code:

#include <bits/stdc++.h>
#define N 200007
#define setIO(s) freopen(s".in","r",stdin) , freopen(s".out","w",stdout)
using namespace std;
struct ques
{
    int l,r,id;
    ques(int l=0,int r=0,int id=0):l(l),r(r),id(id){}
}q[N];
struct P
{
    int len,f,ch[27];
    vector<int>v;
}t[N<<1];
char S[N];
vector<int>G[N];
int tot,last,edges,tim,cnt;
int hd[N],to[N],nex[N],st[N],ed[N],size[N],dfn[N],lst[N],C[N],answer[N];
int lowbit(int t)
{
    return t&(-t);
}
void update(int x,int delta)
{
    for(;x<N;x+=lowbit(x)) C[x]+=delta;
}
int query(int x)
{
    int tmp=0;
    for(;x>0;x-=lowbit(x)) tmp+=C[x];
    return tmp;
}
bool cmp(ques a,ques b)
{
    return a.r<b.r;
}
void addedge(int u,int v)
{
    nex[++edges]=hd[u],hd[u]=edges,to[edges]=v;
}
void extend(int c,int id)
{
    if(t[last].ch[c])
    {
        int p=last;
        int q=t[p].ch[c];
        if(t[q].len==t[p].len+1) last=q;
        else
        {
            int nq=++tot;
            t[nq].len=t[p].len+1;
            t[nq].f=t[q].f,t[q].f=nq;
            memcpy(t[nq].ch, t[q].ch, sizeof(t[q].ch));
            for(;p&&t[p].ch[c]==q;p=t[p].f) t[p].ch[c]=nq;
            last=nq;
        }
    }
    else
    {
        int np=++tot,p=last;
        t[np].len=t[p].len+1,last=np;
        for(;p&&!t[p].ch[c];p=t[p].f) t[p].ch[c]=np;
        if(!p) t[np].f=1;
        else
        {
            int q=t[p].ch[c];
            if(t[q].len==t[p].len+1) t[np].f=q;
            else
            {
                int nq=++tot;
                t[nq].len=t[p].len+1;
                t[nq].f=t[q].f,t[q].f=t[np].f=nq;
                memcpy(t[nq].ch, t[q].ch, sizeof(t[q].ch));
                for(;p&&t[p].ch[c]==q;p=t[p].f) t[p].ch[c]=nq;
            }
        }
    }
    t[last].v.push_back(id);
}
void dfs(int u)
{
    dfn[u]=st[u]=++tim;
    for(int i=hd[u];i;i=nex[i])
    {
        int v=to[i];
        dfs(v);
    }
    for(int k=0;k<t[u].v.size();++k)
        G[dfn[u]].push_back(t[u].v[k]);
    ed[u]=tim;
}
int main()
{
    last=tot=1;
    int i,j,n,m,k;
    // setIO("input");
    scanf("%d%d",&n,&m);
    for(i=1;i<=n;++i)
    {
        last=1;
        scanf("%s",S+1);
        int len=strlen(S+1);
        for(j=1;j<=len;++j) extend(S[j]-‘a‘,i);
    }
    for(i=2;i<=tot;++i)
        addedge(t[i].f,i);
    dfs(1);
    for(i=1;i<=m;++i)
    {
        scanf("%s",S+1);
        int len=strlen(S+1),p=1;
        for(j=1;j<=len;++j)
        {
            int c=S[j]-‘a‘;
            if(!t[p].ch[c])
            {
                p=-1;
                break;
            }
            else p=t[p].ch[c];
        }
        if(p!=-1) q[++cnt]=ques(st[p],ed[p],i);
    }
    sort(q+1,q+1+cnt,cmp);
    for(j=i=1;i<=cnt;++i)
    {
        for(;j<=q[i].r;++j)
        {
            for(k=0;k<G[j].size();++k)
            {
                int tt=G[j][k];
                if(lst[tt]) update(lst[tt],-1);
                lst[tt]=j;
                update(j,1);
            }
        }
        answer[q[i].id]=query(q[i].r)-query(q[i].l-1);
    }
    for(i=1;i<=m;++i) printf("%d\n",answer[i]);
    return 0;
}

  

原文地址:https://www.cnblogs.com/guangheli/p/11577267.html

时间: 2024-07-30 03:56:26

luogu SP8093 后缀自动机+树状数组+dfs序的相关文章

【BZOJ】2434: [Noi2011]阿狸的打字机 AC自动机+树状数组+DFS序

[题意]阿狸喜欢收藏各种稀奇古怪的东西,最近他淘到一台老式的打字机.打字机上只有28个按键,分别印有26个小写英文字母和'B'.'P'两个字母. 经阿狸研究发现,这个打字机是这样工作的: l 输入小写字母,打字机的一个凹槽中会加入这个字母(这个字母加在凹槽的最后). l 按一下印有'B'的按键,打字机凹槽中最后一个字母会消失. l 按一下印有'P'的按键,打字机会在纸上打印出凹槽中现有的所有字母并换行,但凹槽中的字母不会消失. 我们把纸上打印出来的字符串从1开始顺序编号,一直到n.打字机有一个非

BZOJ2434 NOI2011 阿狸的打字机 AC自动机+树状数组+DFS序

题意:给定三个操作:1.在当前字符串的末尾添加一个字符c  2.在当前字符串的末尾删除一个字符  3.记录当前字符串并对其标号.再给出N组询问,每组询问需回答第x个字符串在第y个字符串中出现的次数 题解: 首先按照如下规则建Trie,设当前节点为t,第i个字符串的结尾在Trie中的位置为mark[i]: 1.插入操作:看t是否有c这个儿子,有则t=t->child[c],否则t->child[c]=NewNode,t=t->child[c] 2.删除操作:t=t->father 3

[BZOJ 2434][Noi2011]阿狸的打字机(AC自动机+树状数组+dfs序)

Description 打字机上只有28个按键,分别印有26个小写英文字母和'B'.'P'两个字母.经阿狸研究发现,这个打字机是这样工作的: ·输入小写字母,打字机的一个凹槽中会加入这个字母(这个字母加在凹槽的最后). ·按一下印有'B'的按键,打字机凹槽中最后一个字母会消失. ·按一下印有'P'的按键,打字机会在纸上打印出凹槽中现有的所有字母并换行,但凹槽中的字母不会消失. 例如,阿狸输入aPaPBbP,纸上被打印的字符如下: a aa ab 我们把纸上打印出来的字符串从1开始顺序编号,一直到

UCF Local Programming Contest 2018 E题(树状数组+dfs序)

如果这道题没有一个限制,那么就是一道树状数组+dfs序的裸题 第一个请求或许会带来困惑,导致想要动态建树,如果真的动态修改树,那么dfs序必定会改变,很难维护,并且数据很大,暴力应该会T 所以不妨先把全部的节点建好,这样只需要求一次dfs序,而对于第一种操作 我们只需要再那个位置减去在他之前的dfs序的bouns求和,并在这个的后一个位置+回来,这样就有这个点被修改,并且成为了一个新点,等同于要求的操作 #include<iostream> #include<cstdio> #in

BZOJ 2434: [Noi2011]阿狸的打字机 [AC自动机 Fail树 树状数组 DFS序]

2434: [Noi2011]阿狸的打字机 Time Limit: 10 Sec  Memory Limit: 256 MBSubmit: 2545  Solved: 1419[Submit][Status][Discuss] Description 阿狸喜欢收藏各种稀奇古怪的东西,最近他淘到一台老式的打字机.打字机上只有28个按键,分别印有26个小写英文字母和'B'.'P'两个字母.经阿狸研究发现,这个打字机是这样工作的:l 输入小写字母,打字机的一个凹槽中会加入这个字母(这个字母加在凹槽的最

HDU5293(SummerTrainingDay13-B Tree DP + 树状数组 + dfs序)

Tree chain problem Time Limit: 6000/3000 MS (Java/Others)    Memory Limit: 65536/65536 K (Java/Others)Total Submission(s): 1798    Accepted Submission(s): 585 Problem Description Coco has a tree, whose vertices are conveniently labeled by 1,2,-,n.The

【BZOJ-1103】大都市meg 树状数组 + DFS序

1103: [POI2007]大都市meg Time Limit: 10 Sec  Memory Limit: 162 MBSubmit: 2009  Solved: 1056[Submit][Status][Discuss] Description 在经济全球化浪潮的影响下,习惯于漫步在清晨的乡间小路的邮递员Blue Mary也开始骑着摩托车传递邮件了.不过,她经常回忆起以前在乡间漫步的情景.昔日,乡下有依次编号为1..n的n个小村庄,某些村庄之间有一些双向的土路.从每个村庄都恰好有一条路径到

【BZOJ3653】谈笑风生 离线+树状数组+DFS序

[BZOJ3653]谈笑风生 Description 设T 为一棵有根树,我们做如下的定义: ? 设a和b为T 中的两个不同节点.如果a是b的祖先,那么称“a比b不知道高明到哪里去了”. ? 设a 和 b 为 T 中的两个不同节点.如果 a 与 b 在树上的距离不超过某个给定常数x,那么称“a 与b 谈笑风生”. 给定一棵n个节点的有根树T,节点的编号为1 到 n,根节点为1号节点.你需要回答q 个询问,询问给定两个整数p和k,问有多少个有序三元组(a;b;c)满足: 1. a.b和 c为 T

[POI2007]MEG-Megalopolis (树状数组,Dfs序)

题目描述 Solution 这道题考试的时候竟然没有仔细想,结果只拿了暴力分... 其实就是一个 DFS序+树状数组. 我们先把用 DFS 把它变成一个序列,同时记录它们的 \(siz\). 那么我们每一次连一条边之后就是对它的子树产生影响. 在树状数组里面维护就好了. 代码 #include<bits/stdc++.h> using namespace std; const int maxn=250008; struct sj{ int to; int next; }a[maxn*2]; i