bzoj 2434: [Noi2011]阿狸的打字机

来源:互联网 发布:淘宝评价晒图怎么删除 编辑:程序博客网 时间:2024/06/05 00:15

bzoj 2434: [Noi2011]阿狸的打字机

线段树,AC自动机应用

题意

初始字串为空,首先给定一系列操作序列,有三种操作:

  1. 在结尾加一个字符
  2. 在结尾删除一个字符
  3. 打印当前字串

然后多次询问第x个打印的字串在第y个打印的字串中出现了几次。

思路

%%%1
%%%2

操作字符串的过程实际是构建Trie的过程。新建字母相当于新加节点,打印字串相当于给标记,删除字母相当于回到爸爸节点。

因为Trie保存的是前缀,而子串相当于前缀的后缀,所以我们可以利用AC自动机的Fail指针:字符串u通过fail指针指向字符串v,表明v是u的子串。但是如果我们对于每个询问都直接处理,复杂度肯定过高。

又fail的性质是每个节点出度是1,所以反过来就是一棵树。u是v的爸爸意味着u代表的字串是v的子串。那么我们处理出fail树的dfs序,然后将询问离线按右端点排序,查询左端点的已经出现的fail树中的儿子个数即可。

维护一个线段树,对原Trie树进行遍历,每访问一个节点,就修改线段树,对线段树中该节点的DFS序起点的位置加上1。每往回走一步,就减去1。如果访问到了一个y字串的末尾节点,枚举询问中每个y串对应的x串,查询线段树中x串末尾节点从DFS序中的起始位置到结束位置的和,并记录答案。

然后。。。我他妈for(int i=0;i<strlen(s);i++)成功把复杂度优化到了n方,然后gg了一晚上。

代码

#include <bits/stdc++.h>#define lson l,mid,rt<<1#define rson mid+1,r,rt<<1|1#define M(a,b) memset(a,b,sizeof(a))using namespace std;const int MAXN=100007;const int oo=0x3f3f3f3f;char input[MAXN];//AC自动机struct AC{    static const int maxnode=100007;    static const int sigma_size=26;    int ch[maxnode][sigma_size], val[maxnode], fail[maxnode], fa[maxnode];    int sz;//节点总数    void init()    {        sz=1;M(ch[0], 0);    }    inline int getidx(char c) { return c-'a'; }    void build()    {        int u=0, n=strlen(input);int tmp=0;        for(int i=0;i<n;i++)        {            if(input[i]=='P') val[++tmp]=u;            else if(input[i]=='B') u=fa[u];            else            {                int c=getidx(input[i]);                if(!ch[u][c])                {                    M(ch[sz], 0);                    ch[u][c]=sz;                    fa[sz++]=u;                }                u=ch[u][c];            }        }    }    void getFail()    {        queue<int> q;        fail[0]=0;        for(int c=0;c<sigma_size;c++)        {            int u=ch[0][c];            if(u) { fail[u]=0;q.push(u); }        }        while(!q.empty())        {            int fr=q.front();q.pop();            for(int c=0;c<sigma_size;c++)            {                int u=ch[fr][c];                if(!u) continue;                q.push(u);                int v=fail[fr];                while(v&&!ch[v][c]) v=fail[v];                fail[u]=ch[v][c];            }        }    }}ac;//线段树int stree[MAXN<<2];void pushup(int rt) { stree[rt]=stree[rt<<1]+stree[rt<<1|1]; }void build() { M(stree, 0); }void update(int pos, int v, int l, int r, int rt){    if(l==r) { stree[rt]+=v;return; }    int mid=(l+r)>>1;    if(pos<=mid) update(pos, v, lson);    else update(pos, v, rson);    pushup(rt);}int query(int L, int R, int l, int r, int rt){    if(L<=l&&r<=R) return stree[rt];    int mid=(l+r)>>1;    int res=0;    if(L<=mid) res+=query(L, R, lson);    if(R>mid) res+=query(L, R, rson);    return res;}//fail树int head[MAXN];struct Edge{    int to, ne;    Edge() {}    Edge(int a, int b) { to=a, ne=b; }};Edge e[MAXN];int sume=0;void addedge(int u, int v){    e[sume]=(Edge(v, head[u]));head[u]=sume;    sume++;}int in[MAXN], ou[MAXN], dfsnum=0;void dfs(int u, int fa){    in[u]=++dfsnum;    for(int i=head[u];~i;i=e[i].ne)    {        int v=e[i].to;        if(v==fa) continue;        dfs(v, u);    }    ou[u]=dfsnum;}//离线询问int hq[MAXN];struct Query{    int x, y;    int ne;    int res;    void rd(int _i)    {        scanf("%d%d", &x, &y);        res=0;ne=hq[y];        hq[y]=_i;    }}q[MAXN];void solve(int n){    int curpos=0;int tmp=1;    int len=strlen(input);    for(int i=0;i<len;i++)    {        if(input[i]=='P')        {            for(int k=hq[tmp];~k;k=q[k].ne)            {                int nodenum=ac.val[q[k].x];                q[k].res=query(in[nodenum], ou[nodenum], 1, n, 1);            }            tmp++;        }        else if(input[i]=='B')        {            update(in[curpos], -1, 1, n, 1);            curpos=ac.fa[curpos];        }        else        {            curpos=ac.ch[curpos][input[i]-'a'];            update(in[curpos], 1, 1, n, 1);        }    }}int main(){    scanf("%s", input);    ac.init();sume=0;M(head, -1);build();M(hq, -1);    dfsnum=0;    ac.build();    ac.getFail();    for(int i=0;i<ac.sz;i++)    {        int u=i, v=ac.fail[i];        if(u!=v) addedge(v, u);    }    dfs(0, -1);    int n=dfsnum;    int m;scanf("%d", &m);    for(int i=1;i<=m;i++)        q[i].rd(i);    solve(n);    for(int i=1;i<=m;i++)    {        printf("%d\n", q[i].res);    }    //system("pause");    return 0;}
原创粉丝点击