題目鏈接:http://www.lydsy.com/JudgeOnline/problem.php?id=1717
可以說是後綴數組模板題吧,求出height[]後用一個單調隊列維護最近的k-1個height中的最小值,找出其中的最大的即可。(爲什麼那麼多人都用二分而不用單調隊列呢?)
#include <iostream>
#include <cstring>
#include <cstdio>
#include <string>
#define REP(i, n) for (int i = 0; i < (n); ++i)
#define REP1(i, n) for (int i = 1; i <= (n); ++i)
#define FOR(i, a, b) for (int i = (a); i <= (b); ++i)
#define CLR(x, n) memset(x, n, sizeof(x))
#define maxN 20010
#define maxM 1000010
using namespace std;
void setIO(string name) {
string in_f = name + ".in";
string out_f = name + ".out";
freopen(in_f.c_str(), "r", stdin);
freopen(out_f.c_str(), "w", stdout);
}
struct List {
int v, next;
} d[maxN];
int head[maxM];
int n, k;
int s[maxN];
int rank[maxM], sa[maxN], pos[maxN], val[maxN][2];
int height[maxN];
inline void add_value(int u, int v, int now) {
d[now].v = v;
d[now].next = head[u];
head[u] = now;
}
inline void radix_sort(int c) {
c = (c == 1 ? maxM - 9 : n);
for (int k = 1; k >= 0; --k) {
CLR(head, 0);
for (int i = n; i; --i) add_value(val[pos[i]][k], pos[i], i);
int t = 0;
FOR(i, 0, c) {
for (int p = head[i]; p; p = d[p].next) pos[++t] = d[p].v;
}
}
int t = 1;
rank[pos[1]] = 1;
FOR(i, 2, n) {
if (val[pos[i]][0] != val[pos[i - 1]][0] || val[pos[i]][1] != val[pos[i - 1]][1]) ++t;
rank[pos[i]] = t;
}
}
inline void get_surffix_array() {
int t = 1;
while ((t >> 1) < n) {
REP1(i, n) {
val[i][0] = rank[i];
val[i][1] = ((i + (t >> 1) > n) ? 0 : rank[i + (t >> 1)]);
pos[i] = i;
}
radix_sort(t);
t <<= 1;
}
REP1(i, n) sa[rank[i]] = i;
}
inline void get_common_prefix() {
int h[maxN] = {0};
REP1(i, n) {
if (rank[i] == 1) {
h[i] = 0;
continue;
}
if (i > 1 && h[i - 1]) h[i] = h[i - 1] - 1;
while (i + h[i] <= n && sa[rank[i] - 1] + h[i] <= n && s[i + h[i]] == s[sa[rank[i] - 1] + h[i]]) ++h[i];
height[rank[i]] = h[i];
}
}
inline void init() {
scanf("%d%d", &n, &k);
REP1(i, n) {
scanf("%d", s + i);
++s[i];
rank[i] = s[i];
}
get_surffix_array();
get_common_prefix();
}
inline void solve() {
int que[maxN], l, r;
int ans = 0;
l = r = 0;
REP1(i, k - 1) {
while (l < r && height[que[r - 1]] >= height[i]) --r;
que[r++] = i;
}
FOR(i, k, n) {
while (l < r && height[que[r - 1]] >= height[i]) --r;
que[r++] = i;
while (que[l] < i - k + 2) ++l;
if (height[que[l]] > ans) ans = height[que[l]];
}
printf("%d\n", ans);
}
int main() {
setIO("bzoj1717");
init();
solve();
return 0;
}
好吧,雖說是模板題,但還是調了好長時間(一開始忘了發現radix_sort()裏面第二個循環應該從大到小枚舉),不過1A還是很讓人高興的。
status裏面好多0ms的,而我卻用了372ms,估計是因爲後綴數組的模板用的沒有別人好吧(羅穗騫同學的那一堆for循環模板真心背不出來)
UPD: 這個模板確實是可以優化的,考慮到在radix_sort()中,每一次循環都對head進行了一次重置,這是相當費時的。所以可以考慮在基數排序的過程中,每完成一次對i的排序,就令head[i]=0.原來的模板在poj上跑500ms,而新模板只要110ms.但是沒有弄懂的是,爲什麼當我把add_value()過程寫進radix_sort()裏面時(即減少調用add_value過程的用時)卻要157ms
UPD2: 又發生一件極不科學的事:此題沒用到遞歸,但把inline都去掉後竟然只要63ms!