题意:给出一串序列,序列可循环,请你求出长度为n的最小子串
输入 #1
10
10 9 8 7 6 5 4 3 2 1
输出 #1
1 10 9
这道题显然可以用最小表示法做,但也是一道不错的后缀自动机入门题。下面给出两个算法的处理速度。
最小表示法:
后缀自动机:
可以看到最小表示法速度上很优秀,但是后缀自动机也能过得很稳。这里不再介绍最小表示法~~(因为比较简单)~~
下面的做法需要有后缀自动机的基础,需要入门的可以看看这个dalao的博客:https://www.cnblogs.com/zjp-shadow/p/9218214.html
下面我们分析一下后缀自动机的做法,其实就是建出来就行 。
由于后缀自动机的特性我们可以得知,
1,从一个点开始按着边走必然能走过一个字串
2,每个状态存储的转移数组(像trie一样的数组)所存储的内容,就是下一跳的状态
3,map有内部有序且能存下pair的特性
至此,我们当然会选择map数组来代替普通转移数组,map.first存储边,map.second存储经过这条边到达的状态。
所以我们建好自动机之后就可以顺着每个状态的最小边直接取n个数输出就好了。
还有一点,喜欢用结构体的好像存不下吗,这里换用数组模拟。
//
// Created by SANZONG on 2020/11/30.
//
#include "bits/stdc++.h"
#define mem(x,i) memset(x,i,sizeof(x))
using namespace std;
const int MAXN = 3e5+10;
int len[MAXN<<2];
map<int,int> ch[MAXN<<2];
int fa[MAXN<<2];
int last = 1;
int tot = 1;
void add(int c)
{
int p = last;
last = ++tot;
int np = last;
len[np] = len[p]+1;
for (; p&&!ch[p][c] ; p = fa[p]) ch[p][c] = np;
if (!p) fa[np] = 1;
else
{
int q = ch[p][c];
if (len[q] == len[p]+1) fa[np] = q;
else
{
int nq = ++tot;
fa[nq] = fa[q];
ch[nq] = ch[q];
len[nq] = len[p]+1;
fa[np] = fa[q] = nq;
for (;p&& ch[p][c] == q ; p = fa[p]) {
ch[p][c] = nq;
}
}
}
}
int s[MAXN<<1];
int main()
{
// freopen("in.txt","r",stdin);
// freopen("out.txt","w",stdout);
int n;
scanf("%d",&n);
for (int i = 1; i <= n; ++i) {
scanf("%d",&s[i]);
s[i+n] = s[i];
}
for (int i = 1; i <= (n<<1); ++i) {
add(s[i]);
}
int p = 1;
for (int i = 1; i <= n; ++i) {
map<int,int>::iterator q = ch[p].begin();
printf("%d ",q->first);
p = q->second;
}
}