题目描述
这是一道模板题。
读入一个长度为 $n$ 的由小写英文字母组成的字符串,请把这个字符串的所有非空后缀按字典序从小到大排序,然后按顺序输出后缀的第一个字符在原串中的位置。位置编号为 $1$ 到 $n$。
除此之外为了进一步证明你确实有给后缀排序的超能力,请另外输出 $n - 1$ 个整数分别表示排序后相邻后缀的最长公共前缀的长度。
输入格式
一行一个长度为 $n$ 的仅包含小写英文字母的字符串。
输出格式
第一行 $n$ 个整数,第 $i$ 个整数表示排名为 $i$ 的后缀的第一个字符在原串中的位置。
第二行 $n - 1$ 个整数,第 $i$ 个整数表示排名为 $i$ 和排名为 $i + 1$ 的后缀的最长公共前缀的长度。
限制与约定
$1 \leq n \leq 10^5$
时间限制:$1\texttt{s}$
空间限制:$256\texttt{MB}$
似乎各种方法构造后缀数组都能跑过
在Manchery大神的帮助下,终于理解了怎么从sam-->sa tree-->sa的完美转变
首先,反串构造sam,那么他的par树,就是他的sa tree
然后的问题就是怎么用后缀树线性构造后缀数组
显然第一步是要把所有的fa边反向,指向孩子
但直接处理有问题
可以看一个样例
aacca
它对应的后缀树长这样(一下是吴老板的手稿)
我们发现5、6节点都对应1节点,我们用trie树的方法来记录的话,5、6节点都是a开头的,显然有冲突
于是,思考一种解决冲突的方法
吴老板太神啦~!
后面就是按照字典序来一遍dfs
后面就是一些细节的问题了
诸如复制节点的时候,要把对应的位置一起带过去,但要打上标记,以免dfs的sa数组中出现重复的编号
那么,第二问的话,直接在par树上爬到lca即可,直接返回lca的len
#include<cstdio>
#include<iostream>
#include<algorithm>
#include<cstdlib>
#include<cstring>
#include<string>
#include<climits>
#include<vector>
#include<cmath>
#include<map>
#define LL long long
using namespace std;
inline char nc(){
static char buf[100000],*p1=buf,*p2=buf;
if (p1==p2) { p2=(p1=buf)+fread(buf,1,100000,stdin); if (p1==p2) return EOF; }
return *p1++;
}
inline void read(int &x){
char c=nc();int b=1;
for (;!(c>='0' && c<='9');c=nc()) if (c=='-') b=-1;
for (x=0;c>='0' && c<='9';x=x*10+c-'0',c=nc()); x*=b;
}
inline void read(LL &x){
char c=nc();LL b=1;
for (;!(c>='0' && c<='9');c=nc()) if (c=='-') b=-1;
for (x=0;c>='0' && c<='9';x=x*10+c-'0',c=nc()); x*=b;
}
inline int read(char *s)
{
char c=nc();int len=0;
for(;!((c>='A' && c<='Z')||(c>='a' && c<='z'));c=nc()) if (c==EOF) return 0;
for(;((c>='A' && c<='Z')||(c>='a' && c<='z'));s[len++]=c,c=nc());
s[len++]='\0';
return len;
}
inline void read(char &x){
for (x=nc();!(x=='?' || x=='+' || x=='-');x=nc());
}
int wt,ss[19];
inline void print(int x){
if (x<0) x=-x,putchar('-');
if (!x) putchar(48); else {
for (wt=0;x;ss[++wt]=x%10,x/=10);
for (;wt;putchar(ss[wt]+48),wt--);}
}
inline void print(LL x){
if (x<0) x=-x,putchar('-');
if (!x) putchar(48); else {for (wt=0;x;ss[++wt]=x%10,x/=10);for (;wt;putchar(ss[wt]+48),wt--);}
}
int n,m,s,b[200010],c[200010],d[200010];
char sx[200010],sy[200010];
struct data
{
int len,fa,letter[26],tree[26],id,flag;
}a[200010];
int sa[200010],rank[200010],RANK;
void Extend(int x,int p)
{
s++;int q=s;a[q].len=a[p].len+1;
while (p!=0 && a[p].letter[x]==0)
a[p].letter[x]=q,p=a[p].fa;
if (p==0) {a[q].fa=1;return ;}
int np=a[p].letter[x];
if (a[np].len==a[p].len+1) a[q].fa=np;
else
{
s++;int nq=s;a[nq].len=a[p].len+1;
for (int i=0;i<26;i++)
a[nq].letter[i]=a[np].letter[i];
a[nq].id=a[np].id;
a[nq].fa=a[np].fa;a[np].fa=nq;a[q].fa=nq;
while (p!=0&&a[p].letter[x]==np)
a[p].letter[x]=nq,p=a[p].fa;
}
}
void Insert(char x[])
{
int y=strlen(x);
s=1;int z=1;
for (int i=y-1;i>=0;i--)
Extend(x[i]-'a',z),z=a[z].letter[x[i]-'a'],a[z].id=i+1,d[i+1]=z,a[z].flag=1;
}
void dfs(int x)
{
if(a[x].id!=0 && a[x].flag) sa[++RANK]=a[x].id,rank[a[x].id]=RANK;
for (int i=0;i<26;i++)
if (a[x].tree[i]!=0) dfs(a[x].tree[i]);
}
void build()
{
for (int i=1;i<=s;i++)
c[a[i].len]++;
for (int i=1;i<=s;i++)
c[i]+=c[i-1];
for (int i=1;i<=s;i++)
b[c[a[i].len]--]=i;
for (int i=s;i>=1;i--)
{
int p=b[i];
a[a[p].fa].tree[sx[a[p].id+a[a[p].fa].len-1]-'a']=p;
}
RANK=0;
dfs(1);
}
LL Query(int x,int y)
{
if (x==y) return a[x].len;
if (a[x].len>a[y].len) return Query(a[x].fa,y);
else return Query(x,a[y].fa);
}
int main()
{
n=read(sx);n=strlen(sx);
Insert(sx);
build();
for (int i=1;i<n;i++)
print(sa[i]),putchar(' ');
print(sa[n]);puts("");
for (int i=1;i<n-1;i++)
print(Query(d[sa[i]],d[sa[i+1]])),putchar(' ');
if (n>1) print(Query(d[sa[n-1]],d[sa[n]])),puts("");
return 0;
}