2018.12.15【UOJ35】后缀排序(各种做法(包括SAM))

本文详细介绍了一种使用SAM(Suffix Automaton Method)构建后缀数组的方法,通过在反串上构建SAM,标记每一个反串的前缀终止位置,保留fail树,最终形成原串的后缀树。在后缀树上按字典序DFS回收标记,完成后缀数组的构建。文中提供了详细的代码实现,帮助读者深入理解SAM算法。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

传送门


解析:

SAM以外的其他做法:https://blog.youkuaiyun.com/zxyoi_dreamer/article/details/84667881

SAM做法:

在反串上建出SAM,标记每一个反串的前缀终止位置(即原串的后缀起始位置),保留fail树。

留下来的fail树就是原串的后缀树。

在后缀树上按照字典序进行DFS将遇到的标记一一收回,就完成了后缀数组的构建。

没听懂?回去学习后缀树


代码:

#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define re register
#define gc getchar
#define pc putchar
#define cs const

cs int N=100005;
namespace SA{
	char s[N];
	int sa[N],rk[N],ht[N],len,cnt; 
	typedef struct SAM_node *point;
	struct SAM_node{
		int len,pos;bool tag;
		point fa,son[26],trans[26];
		SAM_node():len(0),pos(0),tag(0),fa(NULL){memset(son,0,sizeof son);}
	};
	namespace SAM{
		SAM_node nd[N<<1];
		point now(nd),last(nd);
		inline void push_back(char c,int pos){
			c-='a';
			point cur=++now;
			cur->len=last->len+1;
			cur->tag=true;
			cur->pos=pos;
			point p=last;
			for(;p&&!p->son[c];p=p->fa)p->son[c]=cur;
			if(!p)cur->fa=nd;
			else if(p->son[c]->len==p->len+1)cur->fa=p->son[c];
			else {
				point clone=++now,q=p->son[c];
				*clone=*q;
				clone->len=p->len+1;
				clone->tag=false;
				q->fa=cur->fa=clone;
				for(;p&&p->son[c]==q;p=p->fa)p->son[c]=clone;
			}
			last=cur;
		}
	};
	
	inline void dfs(point now){
		if(now->tag)sa[rk[now->pos]=++cnt]=now->pos;
		for(int re i=0;i<26;++i)
		if(now->trans[i])dfs(now->trans[i]);
	} 
	
	inline void init(){
		for(int re i=len;i;--i)SAM::push_back(s[i],i);
		for(point re i=SAM::nd+1;i<=SAM::now;++i)i->fa->trans[s[i->pos+i->fa->len]-'a']=i;
		dfs(SAM::nd);
		for(int re i=1,k=0,j;i<=len;ht[rk[i++]]=k)
		for(k?--k:0,j=sa[rk[i]-1];s[i+k]==s[j+k];++k);
	}
}

signed main(){
	scanf("%s",SA::s+1);
	SA::len=strlen(SA::s+1);
	SA::s[SA::len+1]='\0';
	SA::init();
	for(int re i=1;i<=SA::len;++i)printf("%d ",SA::sa[i]);pc('\n');
	for(int re i=2;i<=SA::len;++i)printf("%d ",SA::ht[i]);pc('\n');
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值