题目描述

这是一道模板题。

读入一个长度为 $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;
}