1. 程式人生 > >hdu1403(後綴數組模板)

hdu1403(後綴數組模板)

基數排序 自減 names main int tdi sum 當前 維護

題目鏈接: http://acm.hdu.edu.cn/showproblem.php?pid=1403

題意: 給出兩個字符串, 求他們的最長公共子串

思路: 兩個字符串的最長公共子串長度顯然就是兩個字符串的所有後綴中的最長公共前綴長度. 可以先用一個沒有出現的字符(便於後面區分後綴是否屬於相同字符串)將兩個字符串連成一個字符串,再用後綴數組求其height, SA數組, 對於當前 i, 通過 SA 數組區分後綴 i 和 i - 1 是否在同一個初始字符串中, 若不是則用 height[i] 維護答案最大值.

代碼:

技術分享
 1 #include <iostream>
 2 #include <stdio.h>
 3
#include <string.h> 4 #include <algorithm> 5 #define rank Rank 6 using namespace std; 7 8 const int MAXN = 2e5 + 10; 9 10 char str[MAXN]; 11 int SA[MAXN], rank[MAXN], height[MAXN], sum[MAXN], tp[MAXN]; 12 //rank[i] 第i個後綴的排名, SA[i] 排名為i的後綴的位置, Height[i] 排名為i的後綴與排名為(i-1)的後綴的LCP 13 //sum[i] 基數排序輔助數組, 存儲小於i的元素有多少個, tp[i] rank的輔助數組(按第二關鍵字排序的結果),與SA意義一樣
14 15 bool cmp(int *f, int x, int y, int w){ 16 return f[x] == f[y] && f[x + w] == f[y + w]; 17 } 18 19 void get_SA(char *s, int n, int m){ 20 //先預處理長度為1的情況 21 for(int i = 0; i < m; i++) sum[i] = 0;//清0 22 for(int i = 0; i < n; i++) sum[rank[i] = s[i]]++;//統計每個字符出現的次數 23 for
(int i = 1; i < m; i++) sum[i] += sum[i - 1];//sum[i]為小於等於i的元素的數目 24 for(int i = n - 1; i >= 0; i--) SA[--sum[rank[i]]] = i;//下標從0開始,所以先自減 25 //SA[i]存儲排名第i的後綴下標,SA[--sum[rank[i]]] = i 即下標為i的後綴排名為--sum[rank[i]],這很顯然 26 for(int len = 1; len <= n; len <<= 1){ 27 int p = 0; 28 //直接用SA數組對第二關鍵字排序 29 for(int i = n - len; i < n; i++) tp[p++] = i;//後面i個數沒有第二關鍵字,即第二關鍵字為空,所以最小 30 for(int i = 0; i < n; i++){ 31 if(SA[i] >= len) tp[p++] = SA[i] - len; 32 } 33 //tp[i]存儲按第二關鍵字排序第i的下標 34 //對第二關鍵字排序的結果再按第一關鍵字排序,和長度為1的情況類似 35 for(int i = 0; i < m; i++) sum[i] = 0; 36 for(int i = 0; i < n; i++) sum[rank[tp[i]]]++; 37 for(int i = 1; i < m; i++) sum[i] += sum[i - 1]; 38 for(int i = n - 1; i >= 0; i--) SA[--sum[rank[tp[i]]]] = tp[i]; 39 //根據SA和rank數組重新計算rank數組 40 swap(rank, tp);//交換後tp指向舊的rank數組 41 p = 1; 42 rank[SA[0]] = 0; 43 for(int i = 1; i < n; i++){ 44 rank[SA[i]] = cmp(tp, SA[i - 1], SA[i], len) ? p - 1 : p++;//註意判定rank[i]和rank[i-1]是否相等 45 } 46 if(p >= n) break; 47 m = p;//下次基數排序的最大值 48 } 49 //求height 50 int k = 0; 51 n--; 52 for(int i = 0; i <= n; i++) rank[SA[i]] = i; 53 for(int i = 0; i < n; i++){ 54 if(k) k--; 55 int j = SA[rank[i] - 1]; 56 while(s[i + k] == s[j + k]) k++; 57 height[rank[i]] = k; 58 } 59 } 60 61 int main(void){ 62 char str[MAXN]; 63 while(~scanf("%s", str)){ 64 int len = strlen(str); 65 str[len] = 0; 66 scanf("%s", str + len + 1); 67 int n = strlen(str); 68 str[n] = 0; //末尾添加一個0 69 get_SA(str, n, z + 1); 70 int sol = 0; 71 for(int i = 1; i < n; i++){ 72 if(SA[i] > len && SA[i - 1] < len) sol = max(sol, height[i]); 73 if(SA[i] < len && SA[i - 1] > len) sol = max(sol, height[i]); 74 } 75 printf("%d\n", sol); 76 } 77 return 0; 78 }
View Code

hdu1403(後綴數組模板)