【后缀自动机】poj1509 Glass Beads

字符串最小表示 后缀自动机 O(n)

把串复制一次,链接在后面之后,建立SAM,贪心地在SAM上转移,每次贪心地选择最小的字符,转移的长度为n时停止。

输出时由于要最靠前的,所以要在endpos集合中挑一个最小的,这个在slink_tree上递推一下就能轻松获得。

#include<cstdio>
#include<algorithm>
#include<cstring>
using namespace std;
#define MAXL 10000
#define MAXC 26
int v[2*MAXL+10],__next[2*MAXL+10],first[2*MAXL+10],e;
void AddEdge(int U,int V){
	v[++e]=V;
	__next[e]=first[U];
	first[U]=e;
}
char s[MAXL*2+10];//文本串
int len/*文本串长度*/;
struct SAM{
	int minendpos[2*MAXL+10];
	int n/*状态数0~n-1*/,maxlen[2*MAXL+10],minlen[2*MAXL+10],trans[2*MAXL+10][MAXC],slink[2*MAXL+10];
	void clear(){
		memset(maxlen,0,sizeof(maxlen));
		memset(minlen,0,sizeof(minlen));
		memset(trans,0,sizeof(trans));
		memset(slink,0,sizeof(slink));
		memset(minendpos,0x7f,sizeof(minendpos));
		n=0;
	}
	int new_state(int _maxlen,int _minlen,int _trans[],int _slink){
		maxlen[n]=_maxlen;
		minlen[n]=_minlen;
		for(int i=0;i<MAXC;++i){
			if(_trans==NULL){
				trans[n][i]=-1;
			}
			else{
				trans[n][i]=_trans[i];
			}
		}
		slink[n]=_slink;
		return n++;
	}
	int add_char(char ch,int u,int pos){
		if(u==-1){
			return new_state(0,0,NULL,-1);
		}
		int c=ch-'a';
		int z=new_state(maxlen[u]+1,-1,NULL,-1);
		minendpos[z]=pos;
		int v=u;
		while(v!=-1 && trans[v][c]==-1){
			trans[v][c]=z;
			v=slink[v];
		}
		if(v==-1){//最简单的情况,suffix-path(u->S)上都没有对应字符ch的转移
			minlen[z]=1;
			slink[z]=0;
			return z;
		}
		int x=trans[v][c];
		if(maxlen[v]+1==maxlen[x]){//较简单的情况,不用拆分x
			minlen[z]=maxlen[x]+1;
			slink[z]=x;
			return z;
		}
		int y=new_state(maxlen[v]+1,-1,trans[x],slink[x]);//最复杂的情况,拆分x
		slink[y]=slink[x];
		minlen[x]=maxlen[y]+1;
		slink[x]=y;
		minlen[z]=maxlen[y]+1;
		slink[z]=y;
		int w=v;
		while(w!=-1 && trans[w][c]==x){
			trans[w][c]=y;
			w=slink[w];
		}
		minlen[y]=maxlen[slink[y]]+1;
		return z;
	}
	void dfs(int U){
		for(int i=first[U];i;i=__next[i]){
			dfs(v[i]);
			minendpos[U]=min(minendpos[U],minendpos[v[i]]);
		}
	}
	void work_slink_tree(){
		for(int i=1;i<n;++i){
			AddEdge(slink[i],i);
		}
		dfs(0);
	}
}sam;
int T;
int main(){
//	freopen("poj1509.in","r",stdin);
	scanf("%d",&T);
	for(;T;--T){
		sam.clear();
		scanf("%s",s);
		len=strlen(s);
		for(int i=0;i<len-1;++i){
			s[i+len]=s[i];
		}
		int U=sam.add_char(0,-1,0);
		for(int i=0;i<len*2-1;++i){
			U=sam.add_char(s[i],U,i);
		}
		U=0;
		for(int i=0;i<len;++i){
			for(int j=0;j<MAXC;++j){
				if(sam.trans[U][j]!=-1){
					U=sam.trans[U][j];
					break;
				}
			}
		}
		printf("%d\n",sam.minendpos[U]-len+2);
	}
	return 0;
}

转载于:https://www.cnblogs.com/autsky-jadek/p/6637086.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值