先说后缀自动机的做法。
直接把S串复制一遍成SS,然后建立后缀自动机,go边相当于在当前字符的后面插入,而son边可以看作在字符串前面加一个字符。
所以贪心的走字典序最小的边即可,而且根据后缀自动机的构建的性质,是一定可以走够len的,然后输出即可。
人生第一个后缀自动机的代码。
#include<iostream> #include<cstring> #include<algorithm> #include<cmath> #include<map> #include<cstdio> #define N 600003 using namespace std; int n,m,cnt,nq,np,p,q,last,root; map<int,int> ch[N*2]; int fa[N*2],l[N*2],a[N]; void extend(int x) { int c=a[x]; p=last; np=++cnt; last=np; l[np]=x; for (;p&&!ch[p][c];p=fa[p]) ch[p][c]=np; if (!p) fa[np]=root; else { q=ch[p][c]; if (l[q]==l[p]+1) fa[np]=q; else { nq=++cnt; l[nq]=l[q]+1; ch[nq]=ch[q]; fa[nq]=fa[q]; fa[q]=fa[np]=nq; for (;ch[p][c]==q;p=fa[p]) ch[p][c]=nq; } } } int main() { scanf("%d",&n); for (int i=1;i<=n;i++) scanf("%d",&a[i]); for (int i=1;i<=n;i++) a[n+i]=a[i]; m=2*n; last=root=++cnt; ch[N].clear(); for (int i=1;i<=m;i++) extend(i); p=1; map<int,int>::iterator t; for (int i=1;i<=n;i++) { t=ch[p].begin(); p=t->second; if (i!=n) printf("%d ",t->first); else printf("%d ",t->first); } }
然后再来说说最小表示法,大概的思想就是用两个指针来比较,然后不相等的时候就会排除很大的一片区间,然后最后剩下的就是最优解,最大表示法同理,写完后跑的飞快。
#include <cstdio> #include <cstring> #include <iostream> #include <algorithm> using namespace std; #define F(i,j,k) for (int i=j;i<=k;++i) #define D(i,j,k) for (int i=j;i>=k;--i) #define maxn 300005 int n,a[maxn],pos; int main() { scanf("%d",&n); F(i,1,n) scanf("%d",&a[i]); int i=1,j=2,k=0,flag=0; while (i<=n&&j<=n) { k=0; // printf("%d %d ",i,j); while (a[(i+k-1)%n+1]==a[(j+k-1)%n+1]&&k<=n) k++; if (k==n) {pos=i;flag=1;} else { if (a[(i+k-1)%n+1]>a[(j+k-1)%n+1]) { if (i+k+1<=j) i=j+1; else i=i+k+1; } else { if (j+k+1<=i) j=i+1; else j=j+k+1; } } } if (!flag) pos=min(i,j); F(i,1,n) printf("%d%c",a[(pos+i-2)%n+1],i==n?' ':' '); }