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)\)。

浙公网安备 33010602011771号