HDU 4270 SAM 后缀自动机

来源:互联网 发布:消费金融 知乎 编辑:程序博客网 时间:2024/06/08 01:50
题目:给出一个串,有3种操作,
操作1:在原串后面,添加一个串
操作2:查询长度为len的子串或者长度小于等于len的后缀中字典序最小的

操作3:删除最后的len个字符


关于删除

首先明确一点:  新建的点只与模板点有关系

由新加入的点7产生的点8是根据点3为模板进行复制的(复制所有信息,包括位置)。那么点8是有点3控制的。

可以从两个图看出,删除点7,不删除点8也是可以达到第一个图的效果。

只是图不是最简的SAM。即a-a-b的父亲变为了a-b。但是无关大雅。

从这个可以看出新建立的辅助点是根据某点为模板,并且为这个模板分担了一些线路的点。

模板点控制这这些辅助点。当且仅当这个模板点被删除后,以这个模板点为模板扩展出去的辅助点才会也才能删除。


声明一个bool is_del[]

结构体里加上一个 bool *d 指针

字符串里的每一个结点对应一个域,即d=is_del[x],那么其复制出来的点也共享这个is_del[x]

当这个字符被删除后,也就是说这个域被删除后,所有的点的*d==0  ,全部删除了


删除从最后一个字符(域)开始删除,删除len个字符(域)即可

删除完之后last需要更新到删除后的第一个点


关于询问

node记录下该点的位置,如果这个状态是后缀,则ans=seq_cnt-i+1

否则为ans=p->pos-i+1

#include <iostream>#include <cstring>#include <cstdio>#include <string>using namespace std;const int maxn=200005;struct suffixautomaton{    struct node    {        int len,suf;//到这个状态允许的最大长长度,即max(s)        node *f,*ch[26];  //如果suf==v_idx,则意味这个状态是后缀子串        bool *d;  //        int pos;  //字符原来的位置        node(){}        node(int l)        {            len=l;            f=NULL;            memset(ch,0,sizeof(ch));        }    };    node *root,*last;    node pool[maxn*2];  //储蓄结点用的    node *seq[maxn];  //用于储存输入进来的字符串,,其实就是储存pool数组中主心轴的点。    bool is_del[maxn*2];  //实际上is_del[i] 是代表第i个字符被删除没。node里的 *d是某一个is_del的地址,同一个域里的点共享一个is_del,同在一个域里面的点共享一个is_del[x].    int del_cnt,seq_cnt;  //is_del数组的大小,seq数组的大小    int cnt;             //结点的数量    int v_idx;        //此处的v_idx方便设置suf,避免出错    void init()    {        v_idx=0;        memset(is_del,0,sizeof(is_del));        del_cnt=seq_cnt=0;        root=last=pool;        seq[0]=root;    //此处必须先设置第0位root,避免数组越界        memset(root,0,sizeof(node));        cnt=1;    }    node * new_node(int l=0)    {        node *x=pool+cnt++;        memset(x,0,sizeof(node));        if(l!=0) x->len=l;        return x;    }    void add(char ch)    {        int c=ch-'a';        node *p=last,*np=new_node(last->len+1);        np->pos=np->len;        np->d=is_del+del_cnt++;        *np->d=0;        seq[seq_cnt=np->len]=np;      //以上四行初始化        last=np;        for(;NULL!=p&&(NULL==p->ch[c]||*p->ch[c]->d);p=p->f)  //如果这个点没有被删除且无下一个点ch            p->ch[c]=np;        if(NULL==p)            np->f=root;        else        {            if(p->ch[c]->len==p->len+1)                np->f=p->ch[c];            else            { //新建立的点是以p->ch[c]为模板复制的点,共享一个is_del[],当且仅当模板点被删除后新建立的点才会被删除。                node *q=p->ch[c],*nq=new_node();                *nq=*q;                nq->len=p->len+1;                q->f=np->f=nq;                for(;NULL!=p&&p->ch[c]==q;p=p->f)                    p->ch[c]=nq;            }        }    }    void get_suf()//如果suf==v_idx,则意味这个状态是后缀子串    {        v_idx++;        node *p=last;        while(p!=NULL)            p->suf=v_idx,p=p->f;        root->suf=0;    }    void del(int len)  //从最后一个域开始删除    {        while(len--)            *seq[seq_cnt--]->d=1;        last=seq[seq_cnt];  //last需要改变    }    void query(int l)    {        get_suf();        node *p=root;        for(int i=1;i<=l;i++)        {            for(int j=0;j<26;j++)                if(!(NULL==p->ch[j]||*p->ch[j]->d))                {                    p=p->ch[j];                    if(i==l)                        cout<<p->pos-i+1<<endl;                    else if(p->suf==v_idx)                    {                        cout<<seq_cnt-i+1<<endl;                        return ;                    }                    break;                }        }    }};suffixautomaton sam;char str[maxn];int main(){    while(scanf("%s",str)!=EOF)    {        sam.init();        for(int i=0;str[i];i++)            sam.add(str[i]);        int q,cas,len;        scanf("%d",&q);        while(q--)        {            scanf("%d",&cas);            if(cas==1)            {                scanf("%s",str);                for(int i=0;str[i];i++)                    sam.add(str[i]);            }            else if(cas==2)            {                scanf("%d",&len);                sam.query(len);            }            else            {                scanf("%d",&len);                sam.del(len);            }        }    }    return 0;}



0 0