给出 个串组成的「标准作文库」。对于任意一个串,如果它的长度不少于 且在标准作文库中出现过,则它是「熟悉」的。对于任意一个串,如果能将它划分为若干个串,使「熟悉」的串的长度超过总长度的 ,则称这个串是「熟悉的文章」,定义 为使这个串成为「熟悉的文章」的最大的 。给出若干个串,求每个串的 值。
链接
题解
对标准做文库中的串建立 SAM,将每个串在 SAM 上运行,求出以每个字符结尾的最长匹配长度 。二分一个 ,设 表示前 个字符划分为若干个串,「熟悉」的串的长度和的最大值,则有
化为这种形式
单调队列优化即可。
代码
#include <cstdio>
#include <cstring>
#include <algorithm>
const int MAXN = 1100000;
const int CHARSET_SIZE = 3;
const double EPS = 1e-6;
struct SuffixAutomaton {
struct Node {
Node *ch[CHARSET_SIZE], *next;
int max;
Node(int max = 0) : ch(), next(NULL), max(max) {}
} *start, *last;
void init() {
start = last = new Node;
}
Node *extend(int c) {
Node *u = new Node(last->max + 1), *v = last;
do {
v->ch[c] = u;
v = v->next;
} while (v && !v->ch[c]);
if (!v) {
u->next = start;
} else if (v->ch[c]->max == v->max + 1) {
u->next = v->ch[c];
} else {
Node *n = new Node(v->max + 1), *o = v->ch[c];
std::copy(o->ch, o->ch + CHARSET_SIZE, n->ch);
n->next = o->next;
o->next = u->next = n;
for (; v && v->ch[c] == o; v = v->next) v->ch[c] = n;
}
last = u;
return u;
}
} sam;
char s[MAXN + 2];
int len, matchLen[MAXN + 1];
inline void match() {
SuffixAutomaton::Node *v = sam.start;
int matched = 0;
for (int i = 1; i <= len; i++) {
int c = s[i] - '0';
while (v != sam.start && !v->ch[c]) {
v = v->next;
matched = v->max;
}
if (v->ch[c]) {
v = v->ch[c];
matched++;
}
matchLen[i] = matched;
// printf("%d, matched = %d\n", c, matched);
}
}
/*
inline bool check(int l) {
// printf("l = %d\n", l);
static int f[MAXN + 1];
std::fill(f + 1, f + len + 1, 0);
for (int i = 1; i <= len; i++) {
f[i] = f[i - 1];
for (int j = i - matchLen[i]; j <= i - l; j++) {
// f[i] = std::max(f[i], f[j] + (i - j));
f[i] = std::max(f[i], f[j] - j + i);
}
// printf("f[%d] = %d\n", i, f[i]);
}
return f[len] + EPS >= len * 0.9;
}
*/
inline bool check(int l) {
static int q[MAXN + 1], f[MAXN + 1];
int *ql = q, *qr = q - 1;
std::fill(f + 1, f + len + 1, 0);
for (int i = 1; i <= len; i++) {
f[i] = f[i - 1];
// Insert curr pos
int curr = i - l;
if (curr >= 0) {
while (ql <= qr && f[curr] - curr > f[*qr] - *qr) qr--;
*++qr = curr;
}
// Remove invalid pos
while (ql <= qr && *ql < i - matchLen[i]) ql++;
// Get best pos
if (ql <= qr) f[i] = std::max(f[i], f[*ql] - *ql + i);
}
return f[len] + EPS >= len * 0.9;
}
int main() {
int n, m;
scanf("%d %d", &n, &m);
sam.init();
while (m--) {
scanf("%s", s + 1);
int len = strlen(s + 1);
for (int i = 1; i <= len; i++) sam.extend(s[i] - '0');
if (m) sam.extend(2);
}
while (n--) {
scanf("%s", s + 1);
len = strlen(s + 1);
match();
/*
check(4);
break;
*/
int l = 0, r = len;
while (l < r) {
int mid = l + (r - l) / 2 + 1;
if (check(mid)) l = mid;
else r = mid - 1;
}
printf("%d\n", l);
}
}