当初合肥区域赛的题(现场赛改了数据范围就暴力过了),可惜当初后缀数组算法的名字都没听过,现在重做下。
i从1到n - 1,每次枚举rank[i]附近的排名,并记录当起点小于i时的LCP(rank[i], d)的最大值,或是在LCP不变时更新起点。
#include<cstdio> #include<iostream> #include<algorithm> #include<cstring> #include<cstdlib> using namespace std; const int N = 110008, INF = 0x3F3F3F3F; int val[N], ws0[N], wa[N], wb[N]; int sa[N], rk[N], height[N]; char str[N]; bool cmp(int str[], int a, int b, int l){ return str[a] == str[b] && str[a + l] == str[b + l]; } void da(char str[], int n, int m){ int *x = wa, *y = wb; memset(ws0, 0, sizeof(ws0)); for(int i = 0; i < n; i++) ws0[x[i] = str[i]]++; for(int i = 1; i < m; i++) ws0[i] += ws0[i - 1]; for(int i = n - 1; i >= 0; i--) sa[--ws0[ x[i]] ] = i; for(int j = 1, p = 1; p < n; j *= 2, m = p){ p = 0; for(int i = n - j; i < n; i++) y[p++] = i; for(int i = 0; i < n; i++) if(sa[i] >= j) y[p++] = sa[i] - j; for(int i = 0; i < n; i++) val[i] = x[ y[i] ]; memset(ws0, 0, sizeof(ws0)); for(int i = 0; i < n; i++) ws0[val[i]]++; for(int i = 1; i < m; i++) ws0[i] += ws0[i - 1]; for(int i = n - 1; i >= 0; i--) sa[--ws0[ val[i] ]] = y[i]; swap(x, y); x[sa[0]] = 0; p = 1; for(int i = 1; i < n; i++)x[sa[i]] = cmp(y, sa[i - 1], sa[i], j)? p - 1:p++; } } void calHeight(char str[], int n){ for(int i = 1; i <= n; i++){ rk[ sa[i] ] = i; } int k = 0; for(int i = 0; i < n; i++){ if(k){ k--; } int j = sa[rk[i] - 1]; while(str[i + k] == str[j + k]){ k++; } height[rk[i]] = k; } } int rmq[N][20], tab[N]; void initaskRMQ(int n){ tab[1] = 0; for(int i = 2; i <= n; i++){ tab[i] = ( (i & (i - 1)) == 0)? tab[i - 1] + 1:tab[i - 1]; } for(int i = 1; i <= n; i++){ rmq[i][0] = height[i]; } for(int i = 1; i <= tab[n]; i++){ for(int j = 1; j <= n + 1 - (1 << i); j++){ int a = rmq[j][i - 1]; int b = rmq[j + (1 <<(i - 1))][i - 1]; rmq[j][i] = min(a, b); } } } int askRMQ(int a, int b){ int k = tab[b -a + 1]; b -= (1 << k ) - 1; return min(rmq[a][k], rmq[b][k]); } int LCP(int a, int b){ if(a > b){ swap(a, b); } return askRMQ(a + 1, b); } int main(){ int t; cin>>t; for(int cas = 1; cas <= t; cas++){ scanf("%s", str); int n = strlen(str); da(str, n + 1, 'z' + 1); calHeight(str, n); initaskRMQ(n); printf("Case #%d: ", cas); printf("%d %d ", -1, str[0]); for(int i = 1; i < n; ){ int st = INF, k = 1; int d = rk[i] + 1; int tp; while(d <= n && (tp = LCP(d, rk[i])) >= k){ if(sa[d] < i){ if(tp > k){ k = tp; st = sa[d]; }else{ if(sa[d] < st){ st = sa[d]; } } } d++; } d = rk[i] - 1; while(d >= 1 && (tp = LCP(d, rk[i])) >= k){ if(sa[d] < i){ if(tp > k){ k = tp; st = sa[d]; }else{ if(sa[d] < st){ st = sa[d]; } } } d--; } if(st < i){ printf("%d %d ", k, st); i += k; }else{ printf("%d %d ", -1, (int)str[i]); i++; } } } return 0; }
加油吧! fighting!!!