1. 程式人生 > >洛谷:P3809 【模板】後綴排序(後綴數組模板)

洛谷:P3809 【模板】後綴排序(後綴數組模板)

編號 base sin align include esp using bit 大小寫

P3809 【模板】後綴排序

題目鏈接:https://www.luogu.org/problemnew/show/P3809

題目背景

這是一道模板題。

題目描述

讀入一個長度為 nn 的由大小寫英文字母或數字組成的字符串,請把這個字符串的所有非空後綴按字典序從小到大排序,然後按順序輸出後綴的第一個字符在原串中的位置。位置編號為 11 到 nn。

輸入輸出格式

輸入格式:

一行一個長度為 nn 的僅包含大小寫英文字母或數字的字符串。

輸出格式:

一行,共n個整數,表示答案。

輸入輸出樣例

輸入樣例#1:
ababa
輸出樣例#1:
5 3 1 4 2

題解:

後綴數組模板題= =哇,後綴數組代碼看了我好久,最終還是似懂非懂的,管他的,先用著/滑稽

其實我感覺後綴數組的思想還是比較好理解的,就是這個代碼,基數排序那裏有點迷,代碼中有很多巧妙的細節吧,比如求前綴和後面倒著來求sa數組,我感覺這是最巧妙的地方了。

還有就是對第一、二關鍵字整體排序那裏,倒序就保證了第一關鍵字從大到小,並且第二關鍵字也是從大到小...

廢話不多說了,直接看代碼吧...註釋裏面寫了一些。

代碼如下:

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 1e6+6; int n; char s[N]; int x[N],y[N],sa[N],c[N]; void Get_sa(int m){ n=strlen(s); for(int i=0;i<m;i++) c[i]=0; for(int i=0;i<n;i++) c[x[i]=s[i]]++;//基數排序基本思想,c數組相當於一個桶 for(int i=1;i<m;i++) c[i]+=c[i-1]; for(int i=n-1;i>=0;i--) sa[--c[x[i]]]=i;//求出sa數組,這裏--c[x[i]]]是用來求出sa數組的下標的,註意弄懂sa數組的含義!!
for(int k=1;k<=n;k++){ int p=0; for(int i=n-k;i<n;i++) y[p++]=i; for(int i=0;i<n;i++) if(sa[i]>=k) y[p++]=sa[i]-k;//第二關鍵字,y[i]表示與第i小的第二關鍵字配對的第一關鍵字位置 for(int i=0;i<m;i++) c[i]=0; for(int i=0;i<n;i++) c[x[i]]++; for(int i=1;i<m;i++) c[i]+=c[i-1]; for(int i=n-1;i>=0;i--) sa[--c[x[y[i]]]]=y[i];//倒序很巧妙,第二關鍵字最大的同時,第一關鍵字也是盡可能大的,sa數組的求法可參見上面 swap(x,y); p=1;x[sa[0]]=0; for(int i=1;i<n;i++) x[sa[i]]=y[sa[i-1]]==y[sa[i]]&&y[sa[i-1]+k]==y[sa[i]+k]?p-1:p++;//分別比較第一關鍵字和第二關鍵字 //這裏x[i]表示的是後綴i的大小為多少,這裏相當於將第一、第二關鍵字合並為第一關鍵字 if(p>=n) break ; m=p; } } int main(){ scanf("%s",s); Get_sa(123); int n=strlen(s); for(int i=0;i<n;i++) cout<<sa[i]+1<<" "; return 0; }

洛谷:P3809 【模板】後綴排序(後綴數組模板)