1. 程式人生 > >CH1402 字尾陣列【Hash】【字串】【二分】

CH1402 字尾陣列【Hash】【字串】【二分】

1402 字尾陣列 0x10「基本資料結構」例題

描述

字尾陣列 (SA) 是一種重要的資料結構,通常使用倍增或者DC3演算法實現,這超出了我們的討論範圍。在本題中,我們希望使用快排、Hash與二分實現一個簡單的 O(n log^2⁡n ) 的字尾陣列求法。詳細地說,給定一個長度為 n 的字串S(下標 0~n-1),我們可以用整數 k(0≤k<n) 表示字串S的字尾 S(k~n-1)。把字串S的所有後綴按照字典序排列,排名為 i 的字尾記為 SA[i]。額外地,我們考慮排名為 i 的字尾與排名為 i-1 的字尾,把二者的最長公共字首的長度記為 Height[i]。我們的任務就是求出SA與Height這兩個陣列。<n) i="" i-1="" p="">

輸入格式

一個字串,長度不超過30萬。

輸出格式

第一行為陣列SA,相鄰兩個整數用1個空格隔開。

第二行為陣列Height,相鄰兩個整數用1個空格隔開,特別地,假設Height[1]=0。

樣例輸入

ponoiiipoi

樣例輸出

9 4 5 6 2 8 3 1 7 0
0 1 2 1 0 0 2 1 0 2

樣例解釋

排名第一(最小)的字尾是9(S[9~9],即字串 i),第二的是字尾4(S[4~9],即字串iiipoi),第三的是字尾5(S[5~9],即字串iipoi)以此類推。Height[2]表示排名第2與第1的字尾的最長公共字首,長度為1,Height[3]表示排名第3與第2的字尾的最長公共字首,長度為2,以此類推。

 

題意:

給一個字串s的所有後綴按字典序排個序得到的就是字尾陣列。求出排名第i的和排名第i-1的最長公共字首長度,為height陣列

思路:

依舊是Hash整個字串,根據Hash值二分找到兩個子串的最長公共子串,以此作為sort的比較依據

發現大佬們都是不用結構體的,寫的很巧妙啊。

這種方法求字尾陣列的複雜度是O(n(logn)^2)

 1 #include <iostream>
 2 #include <set>
 3 #include <cmath>
 4 #include <stdio.h>
 5 #include <cstring>
 6
#include <algorithm> 7 #include <map> 8 using namespace std; 9 typedef long long LL; 10 #define inf 0x7f7f7f7f 11 12 const int maxn = 3e5 + 10; 13 char s[maxn]; 14 unsigned long long H[maxn], p[maxn]; 15 int sa[maxn], rk[maxn], height[maxn], n; 16 17 18 unsigned long long getH(int i, int j) 19 { 20 return H[j] - H[i - 1] * p[j - i + 1]; 21 } 22 23 24 //二分求最長公共字首長度 25 int lcp(int x, int y) 26 { 27 int l = 0, r = min(n - x + 1, n - y + 1); 28 while(l < r){ 29 int mid = (l + r + 1) / 2; 30 if(getH(x, x + mid - 1) == getH(y, y + mid - 1)){ 31 l = mid; 32 } 33 else{ 34 r = mid - 1; 35 } 36 } 37 return l; 38 } 39 40 bool cmp(int x, int y) 41 { 42 int l = lcp(x, y); 43 return s[x + l] < s[y + l]; 44 } 45 46 int main() 47 { 48 scanf("%s", s + 1); 49 n = strlen(s + 1); 50 p[0] = 1; 51 for(int i = 1; i <= n; i++){ 52 sa[i] = i; 53 H[i] = H[i - 1] * 131 + s[i] - 'a' + 1; 54 p[i] = p[i - 1] * 131; 55 } 56 sort(sa + 1, sa + n + 1, cmp); 57 for(int i = 2; i <= n; i++){ 58 height[i] = lcp(sa[i - 1], sa[i]); 59 } 60 for(int i = 1; i <= n; i++){ 61 printf("%d ", sa[i] - 1); 62 } 63 printf("\n"); 64 for(int i = 1; i <= n; i++){ 65 printf("%d ", height[i]); 66 } 67 printf("\n"); 68 69 return 0; 70 }