SAM 做题记录

pigeon->gugugu();

P3804 【模板】后缀自动机 (SAM)

建出 SAM 后 在 parent tree 上 dp,对每个节点算它子树里面叶子节点个数,然后根据题意统计答案即可。

Code
#include<bits/stdc++.h>
using namespace std;
const int N=1000005*3;
struct node{int len,link,to[30];};
int n,hd[N],tot,sz[N];
long long ans;
char s[N];
struct edge{int t,nxt;}es[N<<1];
void add(int u,int v){es[++tot]=(edge){v,hd[u]};hd[u]=tot;}
namespace SAM{
	node state[N];
	int rt=1,last=1,tot=1;
	void extend(int ch){
		int cur=++tot,p,q; 
		sz[cur]=1;
		state[cur].len=state[last].len+1;
		for(p=last;p&&!state[p].to[ch];p=state[p].link)state[p].to[ch]=cur;
		if(!p)state[cur].link=rt;
		else{
			q=state[p].to[ch];
			if(state[p].len+1==state[q].len)state[cur].link=q;
			else{
				int clone=++tot;
				state[clone]=state[q];
				state[clone].len=state[p].len+1;
				state[cur].link=state[q].link=clone;
				for(;p&&state[p].to[ch]==q;p=state[p].link)state[p].to[ch]=clone;
			}
		}
		last=cur;
	}
}
void dfs(int u){
	for(int i=hd[u];i;i=es[i].nxt)dfs(es[i].t),sz[u]+=sz[es[i].t];
	if(sz[u]!=1)ans=max(ans,1LL*sz[u]*SAM::state[u].len);
} 
int main(){
	scanf("%s",s);n=strlen(s);
	for(int i=0;i<n;i++)SAM::extend(s[i]-'a');
	for(int i=2;i<=SAM::tot;i++)add(SAM::state[i].link,i);
	dfs(SAM::rt);
	printf("%lld\n",ans);
	return 0;
}

P1368 【模板】最小表示法

两倍长建 SAM,每次走最小的边,走 \(n\) 次即可。

Code
#include<bits/stdc++.h>
using namespace std;
const int N=300005*3;
struct node{int len,link;map<int,int>to;};
struct Suffix_Automation{
	node state[N];
	int rt=1,last=1,tot=1;
	void extend(int ch){
		int cur=++tot,p,q;
		state[cur].len=state[last].len+1;
		for(p=last;p&&!state[p].to[ch];p=state[p].link)state[p].to[ch]=cur;
		if(!p)state[cur].link=rt;
		else{
			q=state[p].to[ch];
			if(state[p].len+1==state[q].len)state[cur].link=q;
			else{
				int clone=++tot;
				state[clone]=state[q];
				state[clone].len=state[p].len+1;
				state[cur].link=state[q].link=clone;
				for(;p&&state[p].to[ch]==q;p=state[p].link)state[p].to[ch]=clone;
			}
		}
		last=cur;
	}
}SAM;
int n,a[N];
map<int,int>::iterator it;
int main(){
	scanf("%d",&n);
	for(int i=1;i<=n;i++)scanf("%d",&a[i]),SAM.extend(a[i]);
	for(int i=1;i<=n;i++)SAM.extend(a[i]);
	for(int i=1,nw=SAM.rt;i<=n;i++)it=SAM.state[nw].to.begin(),printf("%d ",it->first),nw=it->second;
	return 0;
}

P3804 【模板】后缀自动机 (SAM)

这题貌似和第一题很像,其实处理方法不一样。

根据 \(\operatorname{link}\) 的定义,状态 \(v\) 对应的字符串个数为 \(\operatorname{len}(i)-\operatorname{len}(\operatorname{link}(i))\),那么每次加字符的时候更新就可以了。

Code
#include<bits/stdc++.h>
using namespace std;
const int N=300005*3;
struct node{int len,link;map<int,int>to;};
struct Suffix_Automation{
	node state[N];
	int rt=1,last=1,tot=1;
	long long ans;
	void extend(int ch){
		int cur=++tot,p,q;
		state[cur].len=state[last].len+1;
		for(p=last;p&&!state[p].to[ch];p=state[p].link)state[p].to[ch]=cur;
		if(!p)state[cur].link=rt;
		else{
			q=state[p].to[ch];
			if(state[p].len+1==state[q].len)state[cur].link=q;
			else{
				int clone=++tot;
				state[clone]=state[q];
				state[clone].len=state[p].len+1;
				ans-=state[q].len-state[state[q].link].len;
				state[cur].link=state[q].link=clone;
				ans+=state[q].len-state[state[q].link].len;
				ans+=state[clone].len-state[state[clone].link].len;
				for(;p&&state[p].to[ch]==q;p=state[p].link)state[p].to[ch]=clone;
			}
		}
		ans+=state[cur].len-state[state[cur].link].len;
		last=cur;
	}
}SAM;
int n,a[N];
int main(){
	scanf("%d",&n);
	for(int i=1;i<=n;i++)scanf("%d",&a[i]),SAM.extend(a[i]),printf("%lld\n",SAM.ans);
	return 0;
}

posted @ 2020-09-26 23:52  happydef  阅读(97)  评论(1编辑  收藏  举报