P1368 【模板】最小表示法 题解

省流:使用了万能的 SAM,并且能够做到线性,目前其他题解没有。

Discription

题目

给定一个由数字构成的字符串,求它的所有循环移位中字典序最小的那一个。

Solution

首先有一部转化:一个字符串的所有循环移位,等效于这个字符串复制之后,所有长度等于原长的子串。

接下来就要拿出能够包含全部子串的数据结构—— SAM 。

对给出字符串复制后形成的字符串建好 SAM 后,只需要从源点开始走字符串长度步,每一步贪心地走最小的转移边。

但是现在问题是字符集很大,转移边无法直接开数组。

有两种解决方式:

1. 使用 map

由于 map 的底层实现是红黑二叉树,自然支持访问键值最小的元素对。

只需要:

map<int,int>to;
cout<<(to.begin()->first)<<'\n';//已插入的最小键值
cout<<(to.begin()->second)<<'\n';//已插入的最小键值所对应的value

即可。

代码:

#include<bits/stdc++.h>
#define N 600005
#define F(i,j,k) for(int i=j;i<=k;i++)
using namespace std;
struct state{int link,len;map<int,int>to;}st[N<<1];
int sz,lst,n,a[N];
inline void insert(int c){
	int cur=++sz,p=lst;
	st[cur].len=st[lst].len+1,lst=cur;
	while(p!=-1&&!st[p].to[c])st[p].to[c]=cur,p=st[p].link;
	if(p==-1)return;
	int q=st[p].to[c];
	if(st[p].len+1==st[q].len)return st[cur].link=q,void();
	int clone=++sz;
	st[clone]=st[q],st[clone].len=st[p].len+1;
	while(p!=-1&&st[p].to[c]==q)st[p].to[c]=clone,p=st[p].link;
	st[cur].link=st[q].link=clone;
}
int main(){
	ios::sync_with_stdio(0);
	st[0].link=-1;
	cin>>n;
	F(i,1,n)cin>>a[i],insert(a[i]);
	F(i,1,n)insert(a[i]);
	int u=0;
	F(i,1,n)cout<<st[u].to.begin()->first<<' ',u=st[u].to.begin()->second;
}

然而 map 自带一个 \(\log\) 。因此时间复杂度为 \(O(nlogn)\)

2. 使用 unordered_map

unordered_map 是用哈希表实现的,无法维护键值最小的元素。不过没有关系,由于 SAM 的构建方式,只有连边或更改边,但是没有删边,所以额外维护一下每个节点出边中最小的即可。

#include<bits/stdc++.h>
#define N 600005
#define inf 0x3f3f3f3f
#define F(i,j,k) for(int i=j;i<=k;i++)
using namespace std;
struct state{int link,len,mn;unordered_map<int,int>to;}st[N<<1];
int sz,lst,n,a[N];
inline void get(int &x,int y){x=min(x,y);}
inline void insert(int c){
	int cur=++sz,p=lst;
	st[cur].len=st[lst].len+1,st[cur].mn=inf,lst=cur;
	while(p!=-1&&!st[p].to[c])st[p].to[c]=cur,get(st[p].mn,c),p=st[p].link;
	if(p==-1)return;
	int q=st[p].to[c];
	if(st[p].len+1==st[q].len)return st[cur].link=q,void();
	int clone=++sz;
	st[clone]=st[q],st[clone].len=st[p].len+1;
	while(p!=-1&&st[p].to[c]==q)st[p].to[c]=clone,p=st[p].link;
	st[cur].link=st[q].link=clone;
}
int main(){
	ios::sync_with_stdio(0);
	st[0].link=-1,st[0].mn=inf;
	cin>>n;
	F(i,1,n)cin>>a[i],insert(a[i]);
	F(i,1,n)insert(a[i]);
	int u=0;
	F(i,1,n)cout<<st[u].mn<<' ',u=st[u].to[st[u].mn];
}

时间复杂度 \(O(n)\)

posted @ 2025-06-17 11:58  linjingxiang  阅读(9)  评论(0)    收藏  举报