トピックリンク
ソル
タイトルの意味は明確であるが、それは非常に良いの需要はないようです。またはポイントの一部を見てみましょう。
10%のデータ
\(\シータ(N-2 ^ \ n型ログ)\) 、暴力、10分を務めました。
データの別の20%
私は比較に、見つかった一つのこと\(S_I \)と\(S_j \) 、それが想定され、\(私はJ \を<) 1からI-1には、J + 1からnまで、この期間と同じです同じです私たちは元の文字列とI + 1〜jの私は〜J-1を比較すると、限り、これは2行目です。任意の隣接する2つが同じではないので、することが可能である(\シータ(1)\ \ ) を比較します。
データの別の30%
考えてハッシュ、ハッシュだけの文章のような、しかし限り、私たちは二つの文字列の最長の共通のプレフィックスを見つけると約半分、その後、ライン上の次の最長の共通のプレフィックスの位置を比較しているが。時間の複雑さ:\(\シータ(N- \ログイン^ 2N)\)
最後に、データの40%
それは2つのサフィックス、すぐに思った接尾辞配列の最長の共通のプレフィックスに非常に明確にする必要があります。\(NログN \ \)の前処理をすることができる(\ \シータ(1)\ ) と比較し再利用\(\シータ(Nログ\ n)を\) クイックドレーンのこと。
発見:TLE 60分
サフィックスアレイグループは、定数が通過するには大きすぎるようです。しかし、あなたが設定することができ、我々は、隣接する二つの接尾辞の最長の共通のプレフィックスを必要とするものについて考える(H_I \)は\先頭にサフィックスIとI + 1に最長共通のプレフィックスの長さである、それはだった明白な\(H_I> H_-I 1 = {} -1 \)、2文字の除去と同等の始まりである\(H_I \)割り当て始まる(I-H_ {-1} \ 1 \)は、することができる(\ \シータ(N)\)配列hを前処理し、そして最後に直接ソート。実行が、しかし、多くの大物\(\シータ(N)\ ) アルゴリズム、だけでなく、経由。
コード:
#include<bits/stdc++.h>
using namespace std;
#define cmp(a,b,c) (a+c<=n&&b+c<=n&&y[a]==y[b]&&y[a+c]==y[b+c])
#define min(a,b) (a<b?a:b)//卡常
const int maxn=1e6+10;
char s[maxn];
int ans[maxn],h[maxn],n;
inline void write(int x) {//卡常
if(x>=10)write(x/10);
putchar(x%10^48);
}
void Swap(int &a,int &b) {//还是卡常
a^=b,b^=a,a^=b;
}
int cmp1(int a,int b) {//自定义比较方法
int f=0;
if(a>b)Swap(a,b),f=1;
if(h[a]>=b-a)return f^1;
return (s[a+h[a]+1]<s[a+h[a]])^f;
}
int t[maxn];
void Qsort(int l,int r) {//手写归并排序,也是写后缀数组卡常留下的
if(l==r)return;
int mid=(l+r)>>1;
Qsort(l,mid),Qsort(mid+1,r);
int i=l,j=mid+1,cnt=0;
while(i<=mid||j<=r) {
if((j>r)||(i<=mid&&cmp1(ans[i],ans[j])))t[cnt++]=ans[i++];
else t[cnt++]=ans[j++];
}
for(int i=0; i<cnt; i++)ans[i+l]=t[i];
}
int main() {
scanf("%d\n",&n);
fread(s+1,1,n,stdin);//优读,写后缀数组卡常留下的
for(int i=1; i<n; i++) {//求h数组
h[i]=max(h[i-1]-1,0);
for(int j=i+1; s[i+h[i]]==s[j+h[i]]; h[i]++);
}
for(int i=1; i<=n; i++)ans[i]=i;//初始化
Qsort(1,n);
for(int i=1; i<n; i++)write(ans[i]),putchar(' ');
write(ans[n]),putchar('\n');
return 0;
}