题目:给出一个串,有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;
}