做法

对于字符串的第 个字符,定义对偶函数 ,其含义为:对于原串的第 个字符,找到它之后与它相同的字符位置 ,结果的第 个元素即为 。如果不存在这样的字符,相应的结果为
就像题中的函数 ,一个字符串的函数 就是由每个位置的 组成。
定义 上的小于关系为字典序从大到小,并且前缀优先。

举例:

按照对偶函数的顺序构造后缀数组即可。

举例:
以串 abaabaaaabba 为例:

a
ba
baaaabba
abba
baabaaaabba
abaaaabba
abaabaaaabba
bba
aabba
aabaaaabba
aaabba
aaaabba
答案即为列

证明

详细证明见论文 Parameterized Suffix Arrays for Binary Strings。以下大致描述该论文的思路。

我们只需证明, 存在对应的偏序关系。即

那么 时显然。下面考虑小于的情况。

  • 如果 的前缀,那么 要么是 的前缀,要么把所有 中的 互相代换之后是 的前缀。反之亦然。

    证明:
    反证。不妨设
    那么 中必然有一个为 中必然没有
    与 「 的前缀」相矛盾。
    另一侧的证明显然。

    由此,显然对于任意的 ,要么 ,要么 。即 要么是 的前缀,要么存在

  • 如果 不是 的前缀。那么必然存在
    记满足 的串为 ,满足 的串为
    存在如下没有前缀关系的情况(以下假设 ):

    • pos 1 2 i-1 i i+1
      0 1 1 1 0
      0 1 1 1 1
      1 1 1 sth>1 or sth
      1 1 1 1 1 or sth

      可知

    • pos 1 2 ... i-1 i i+1
      0 1 ... 1 1 0
      0 1 ... 1 1 1
      1 1 ... 1 sth>1 or sth
      1 1 ... 1 1 1 or x

      可知

综上,原命题得证。

时间复杂度

视后缀数组的构造算法,

实现细节

通过令 ,并且加入虚节点 的方式,通过普通后缀数组模板实现排序。

代码

#include <bits/stdc++.h>

using namespace std;

const int N=3000020;

char ts[N];
int s[N];
int n,sa[N],rk[N],oldrk[N << 1],id[N],px[N],cnt[N];

bool cmp(int x,int y,int w) 
{
    return oldrk[x] == oldrk[y] && oldrk[x + w] == oldrk[y + w];
}

void solve()
{
    int l[2]={n+1,n+1};
    for(int i=n;i>0;i--)
    {
        s[i]=((l[ts[i]-'a']>n?n:l[ts[i]-'a']-i));
        l[ts[i]-'a']=i;
    }
    n++;
    s[n]=n;
    int i,m=n+3,p,w;

    for(int i=0;i<=n+5;i++)
    {
        sa[i]=rk[i]=oldrk[i]=oldrk[i+n+5]=id[i]=px[i]=cnt[i]=0;
    }

    for(i=1;i<=n;++i) ++cnt[rk[i]=s[i]];
    for(i=1;i<=m;++i) cnt[i]+=cnt[i-1];
    for(i=n;i>=1;--i) sa[cnt[rk[i]]--]=i;

    for(w=1;w<n;w<<=1,m=p) 
    { 
        for(p=0,i=n;i>n-w;--i) id[++p]=i;
        for(i=1;i<=n;++i) if(sa[i]>w) id[++p]=sa[i]-w;
        for(i=0;i<=n;++i) cnt[i]=0;
        for(i=1;i<=n;++i) ++cnt[px[i]=rk[id[i]]];
        for(i=1;i<=m;++i) cnt[i]+=cnt[i-1];
        for(i=n;i>=1;--i) sa[cnt[px[i]]--]=id[i];
        for(i=0;i<=min(N-1,2*n+5);++i) oldrk[i]=rk[i];

        for(p=0,i=1;i<=n;++i) rk[sa[i]]=cmp(sa[i],sa[i-1],w)?p:++p;
    }

    for(i=n-1;i>=1;--i) printf("%d ",sa[i]);
    printf("\n");
}

int main(void)
{
    while(~scanf("%d",&n))
    {
        scanf("%s",ts+1);
        solve();
    }

    return 0;
}