bzoj 2434 fail tree+dfs序

news/2024/7/8 6:37:53

  首先比较明显的是我们可以将字符串组建立ac自动机,那么对于询问s1字符串在s2字符串中出现的次数,就是在以s1结尾为根的fail tree中,子树有多少个节点是s2的节点,这样我们处理fail tree的dfs序,然后用BIT维护,但是如果只是在线处理询问的话会tle,因为每个询问需要将节点的每一位在BIT中都修改,那么我们就浪费了好多性质,因为对于好多字符串拥有较长的LCP,那么我们可以模拟建trie的过程在自动机上跑,每遇到一个添加的字符,就解决所有询问为某字符串在该字符串中出现的次数,这样就可以了。

/**************************************************************
    Problem: 2434
    User: BLADEVIL
    Language: C++
    Result: Accepted
    Time:852 ms
    Memory:39128 kb
****************************************************************/
 
//By BLADEVIL
#include <cstdio>
#include <cstring>
#define maxn 200010
 
using namespace std;
 
struct node{
    int cnt,last,left,right;
    node *child[30],*fail,*father;
    node(){
        cnt=last=left=right=0;
        fail=father=NULL;
        memset(child,0,sizeof child);
    }
}nodepool[maxn],*totnode,*root,*que[maxn],*adr[maxn],*other[maxn],*adrans[maxn];
char c[maxn];
int len,tot,l,sum;
int pre[maxn],bit[maxn];
int ll,preans[maxn],otherans[maxn],lastans[maxn],sizeans[maxn];
int ans[maxn];
 
void add(int x,int y){
    while (x<=sum){
        bit[x]+=y;
        x+=x&(-x);
    }
}
 
int ask(int x){
    int ans=0;
    while (x){
        ans+=bit[x];
        x-=x&(-x);
    }
    return ans;
}
 
void connect(node *x,node *y){
    pre[++l]=x->last;
    x->last=l;
    other[l]=y;
    //printf("%d %d\n",x,y);
}
 
void connectans(int x,int y,int z){
    preans[++l]=lastans[x];
    lastans[x]=l;
    otherans[l]=y;
    sizeans[l]=z;
}
 
void build_trie(){
    totnode=nodepool; root=totnode++;
    scanf("%s",&c); len=strlen(c);
    node *t=root;
    for (int i=0;i<len;i++){
        if (c[i]=='P') adrans[++tot]=t; else
        if (c[i]=='B') t=t->father; else {
            if (!t->child[c[i]-'a']) 
                t->child[c[i]-'a']=totnode++,t->child[c[i]-'a']->father=t;
            t=t->child[c[i]-'a'];
            adr[i]=t;
        };
        //printf("%d ",t);
    }
    //printf("\n");
    //for (int i=0;i<len;i++) printf("%d ",adr[i]);
    //for (node *i=nodepool;i!=totnode;i++) printf("%d %d\n",i,i->father);
}
 
void build_ac(){
    int h=0,t=1;
    que[1]=root; root->fail=root;
    for (int i=0;i<26;i++) if (!root->child[i]) root->child[i]=root;
    while (h<t){
        node *v=que[++h];
        for (int i=0;i<26;i++) if (v->child[i]&&v->child[i]!=root){
            que[++t]=v->child[i];
            node *u=v->fail;
            que[t]->fail=u->child[i]!=que[t]?u->child[i]:root;
        } else v->child[i]=v->fail->child[i];
    }
    //for (int i=1;i<=t;i++) printf("%d %d ",que[i],que[i]->fail); printf("\n");
    //for (int i=1;i<=tot;i++) printf("%d ",adr[i]); printf("\n");
}
 
void dfs(node *x,node *fa){
    //printf("%d %d %d\n",x,fa,sum);
    x->left=++sum;
    for (int p=x->last;p;p=pre[p]){
        if (other[p]==fa) continue;
        dfs(other[p],x);
    }
    x->right=sum;
}
 
/*void work(){
    for (node *i=nodepool;i!=totnode;i++) if (i!=root) connect(i->fail,i);
    dfs(root,NULL);
    //for (node *i=nodepool;i!=totnode;i++) printf("%d %d %d %d\n",i,i->left,i->right,i->father);
    int m;
    scanf("%d",&m);
    while (m--){
        int x,y;
        scanf("%d %d",&x,&y);
        for (node *i=adr[y];i!=root;i=i->father) add(i->left,1);//printf("%d ",i->left);
        //printf("%d %d",adr[x]->left,adr[x]->right);
        //printf("%d ",ask(adr[x]->right));
        printf("%d\n",ask(adr[x]->right)-ask(adr[x]->left-1));
        for (node *i=adr[y];i!=root;i=i->father) add(i->left,-1);
    }
}*/
 
void work(){
    for (node *i=nodepool;i!=totnode;i++) if (i!=root) connect(i->fail,i);
    dfs(root,NULL);
    int m;
    scanf("%d",&m);
    for (int i=1;i<=m;i++){
        int x,y;
        scanf("%d%d",&x,&y);
        connectans(y,x,i);
    }
    int stack[maxn],tot=0,ansy=0;
    memset(stack,0,sizeof stack);
    for (int i=0;i<len;i++){
        if (c[i]=='P'){
            ansy++;
            for (int p=lastans[ansy];p;p=preans[p]){
                ans[sizeans[p]]=ask(adrans[otherans[p]]->right)-ask(adrans[otherans[p]]->left-1);
            }
        } else
        if (c[i]=='B') {
            add(adr[stack[tot--]]->left,-1);
        } else {
            stack[++tot]=i;
            add(adr[i]->left,1);
        }
    }
    for (int i=1;i<=m;i++) printf("%d\n",ans[i]);
}
 
int main(){
    build_trie();
    build_ac();
    work();
    return 0;
}

 

转载于:https://www.cnblogs.com/BLADEVIL/p/3580825.html


http://www.niftyadmin.cn/n/2757439.html

相关文章

JNDI解读(转)

NDI 是什么 JNDI是 Java 命名与目录接口&#xff08;Java Naming and Directory Interface&#xff09;&#xff0c;在J2EE规范中是重要的规范之一&#xff0c;不少专家认为&#xff0c;没有透彻理解JNDI的意义和作用&#xff0c;就没有真正掌握J2EE特别是EJB的知识。 那么&…

Shiro入门(四)Shiro登录验证源码及策略

前言 本章讲解Shiro登录验证的源码剖析以及登录验证策略 方法 一、Shiro登陆验证源码解析 1.使用Subject的login方法验证token 2.实际上Subject类仅仅是一个接口&#xff0c;他通过实现类DelegatingSubject将token委托给SecurityManager 来完成验证 3.而SecurityManager作为…

OCM考点之一外部表管理

一、创建外部表以及产生dmp文件1、创建directory&#xff0c;需要有 create any directory权限&#xff1a;CREATE DIRECTORY admin AS /oracle/admin; 或者创建了diretory后授权read权限&#xff1a;GRANT READ ON DIRECTORY admin TO scott; 2、创建外部表&#xff1a;SQL&…

《从零开始学Swift》学习笔记(Day 56)—— Swift编码规范之命名规范

原创文章&#xff0c;欢迎转载。转载请注明&#xff1a;关东升的博客 程序代码中到处都是自己定义的名字&#xff0c;取一个有样并且符合规范的名字非常重要。命名方法很多&#xff0c;但是比较有名的&#xff0c;广泛接受命名法有&#xff1a;匈牙利命名&#xff0c;一般只是命…

《流量的秘密 Google Analytics网站分析与商业实战》一1.6 有问有答:衡量成功...

本节书摘来自异步社区《流量的秘密 Google Analytics网站分析与商业实战》一书中的第1章&#xff0c;第1.6节&#xff0c;作者 【英】Brian Clifton&#xff0c;更多章节内容可以访问云栖社区“异步社区”公众号查看 1.6 有问有答&#xff1a;衡量成功 http://trends.builtwit…

《途客圈创业记:不疯魔,不成活》一一2.7 愿景和使命

本节书摘来自异步社区出版社《途客圈创业记&#xff1a;不疯魔&#xff0c;不成活》一书中的第2章&#xff0c;第2.7节&#xff0c;作者&#xff1a;陈天&#xff0c;更多章节内容可以访问云栖社区“异步社区”公众号查看。 2.7 愿景和使命 初始的团队组建起来了&#xff0c;我…

从明星扎堆转型互联网 看内容草根时代的结束

在互联网时代&#xff0c;与其说“得屌丝者得天下”&#xff0c;不如说这是一个由千千万万草根支撑起来的繁华时代。作为占据互联网参与者最重要的组成部分&#xff0c;草根此前承担着两大重任。一是内容创造&#xff0c;为互联网不断输入多个领域的内容和信息&#xff1b;另一…

Windows Phone开发(35):使用Express Blend绘图

原文:Windows Phone开发&#xff08;35&#xff09;&#xff1a;使用Express Blend绘图 上一节中我们简单扯了一下绘图指令&#xff0c;然而那也不是最简单的绘图法&#xff0c;今天&#xff0c;我再向大家推荐一种更好的绘图方案——Express Blend工具的使用。 这个工具是随SD…