补充一下:这个东西貌似叫做fail树,也叫后缀指针树,是自动机很重要的东西。
题意:给定一张包含 N 个单词的表,每个单词有个价值 W。要求从中选出一个子序列使得其中的每个单词是后一个单词的子串,最大化子序列中 W 的和。
显然有f[i] = max(f[j] + w[i]), j < i 且串j包含于串i。
将方程化为f[i] = max(f[j] + w[i]), j > i 且串i包含于串j。接着可以考虑用线段树优化去max。
可以用AC自动机或后缀数组做,不过后缀数组由于构建复杂度和二分区间显然要慢得多。
AC自动机code:
#include <cstdio>
#include <cmath>
#include <cstdlib>
#include <cstring>
#include <ctime>
#include <cctype>
#include <map>
#include <set>
#include <string>
#include <vector>
#include <algorithm>
using namespace std;
#ifdef WIN32
#define fmt64 "%I64d"
#else
#define fmt64 "%lld"
#endif
#define PI M_PI
#define oo 0x13131313
#define PB push_back
#define PO pop_back
#define iter iterator
#define MP make_pair
#define fst first
#define snd second
#define cstr(a) (a).c_str()
#define FOR(i, j, k) for (i = (j); i <= (k); ++i)
#define ROF(i, j, k) for (i = (j); i >= (k); --i)
#define FER(i, j, k) for (i = j[k]; i; i = i->n)
#define FRE(i, a) for (i = (a).begin(); i != (a).end(); ++i)
typedef unsigned int uint;
typedef long long int64;
typedef unsigned long long uint64;
typedef long double real;
template<class T> inline bool minim(T &a, const T &b) {return b < a ? a = b, 1 : 0;}
template<class T> inline bool maxim(T &a, const T &b) {return b > a ? a = b, 1 : 0;}
template<class T> inline T sqr(const T &a) {return a * a;}
#define maxn 300005
#define maxk 20005
struct edge {int t; edge *n;}
es[maxn + maxk], *adj, *lst[maxn];
void link(int i, int j)
{
*(++adj) = (edge){j, lst[i]}, lst[i] = adj;
}
struct trie
{
edge *lst;
trie *c[26], *f, *fa;
}
root[maxn], *tt, *que[maxn], *tback[maxk];
void insert(char *st, int k)
{
trie *p = root;
for (; *st; p = p->c[*(st++) - 'a'])
if (!p->c[*st - 'a']) (p->c[*st - 'a'] = ++tt)->fa = p;
*(++adj) = (edge){k, p->lst}, p->lst = adj;
tback[k] = p;
}
void build()
{
int i, ll, rr;
(que[ll = rr = 1] = root)->f = root;
for (; ll <= rr; ++ll)
FOR(i, 0, 25) if (que[ll]->c[i]) {
trie *p = que[ll]->c[i], *q;
for (q = que[ll]->f; q != root && !q->c[i]; q = q->f);
p->f = q->c[i] && q->c[i] != p ? q->c[i] : root;
link(p->f - root, p - root);
que[++rr] = p;
}
}
int T, n, cnt, begin[maxk], end[maxk], mark[maxn];
void dfs()
{
int u, v; trie *p; edge *e;
for (mark[u = 0] = cnt = 0; ; )
if (!lst[u]) {
p = root + u;
for (e = p->lst; e; e = e->n) end[e->t] = cnt;
if (p->f == p) return; else u = p->f - root;
} else {
v = lst[u]->t, lst[u] = lst[u]->n;
u = v, p = root + u, mark[u] = ++cnt;
for (e = p->lst; e; e = e->n) begin[e->t] = cnt;
}
}
struct node
{
node *s, *t, *f;
int l, r, v;
}
ns[maxn*2], *nt, *nRT, *back[maxn];
int nL, nR, ans;
void build(node *&p, int l, int r)
{
p = ++nt, p->l = l, p->r = r, p->v = 0;
if (l == r) {back[l] = p; return;}
build(p->s, l, (l + r) >> 1);
build(p->t, ((l + r) >> 1) + 1, r);
p->s->f = p->t->f = p;
}
void insert(int pos, int k)
{
node *p = back[pos];
if (maxim(p->v, k))
for (p = p->f; p; p = p->f) {
k = max(p->s->v, p->t->v);
if (p->v == k) break; p->v = k;
}
}
void query(node *p)
{
if (nL <= p->l && p->r <= nR) {ans = p->v; return;}
if (nL <= p->s->r && p->s->v > ans) query(p->s);
if (p->t->l <= nR && p->t->v > ans) query(p->t);
}
int ask(int l, int r) {return nL = l, nR = r, ans = 0, query(nRT), ans;}
void prepare()
{
tt = root, memset(root, 0, sizeof root);
adj = es, memset(lst, 0, sizeof lst);
nt = ns;
}
char st[maxn]; int w[maxk];
int main()
{
#ifndef ONLINE_JUDGE
freopen("p3.in", "r", stdin);
freopen("p3.out", "w", stdout);
#endif
for (scanf("%d", &T); T--; ) {
int i, ans = 0;
prepare(), scanf("%d", &n);
FOR(i, 1, n) scanf("%s%d", st, w + i), insert(st, i);
build();
dfs();
build(nRT, 1, cnt);
ROF(i, n, 1) {
int k = ask(begin[i], end[i]) + w[i];
maxim(ans, k);
for (trie *p = tback[i]; p != root; p = p->fa)
insert(mark[p - root], k);
}
printf("%d\n", ans);
}
return 0;
}
后缀数组code:
#include <cstdio>
#include <cmath>
#include <cstdlib>
#include <cstring>
#include <ctime>
#include <cctype>
#include <map>
#include <set>
#include <string>
#include <vector>
#include <algorithm>
using namespace std;
#ifdef WIN32
#define fmt64 "%I64d"
#else
#define fmt64 "%lld"
#endif
#define PI M_PI
#define oo 0x13131313
#define PB push_back
#define PO pop_back
#define iter iterator
#define MP make_pair
#define fst first
#define snd second
#define cstr(a) (a).c_str()
#define FOR(i, j, k) for (i = (j); i <= (k); ++i)
#define ROF(i, j, k) for (i = (j); i >= (k); --i)
#define FER(i, j, k) for (i = j[k]; i; i = i->n)
#define FRE(i, a) for (i = (a).begin(); i != (a).end(); ++i)
typedef unsigned int uint;
typedef long long int64;
typedef unsigned long long uint64;
typedef long double real;
template<class T> inline bool minim(T &a, const T &b) {return b < a ? a = b, 1 : 0;}
template<class T> inline bool maxim(T &a, const T &b) {return b > a ? a = b, 1 : 0;}
template<class T> inline T sqr(const T &a) {return a * a;}
#define maxn 300005
#define maxk 20005
#define maxm 2005
typedef int array[maxn];
typedef int arr[maxk];
array sa, rank, sum, xx, yy, z, height, st;
void build(int &n, int &m)
{
int i, j, k; int *x = xx, *y = yy;
memset(sum, 0, sizeof(array));
FOR(i, 1, n) ++sum[x[i] = st[i]];
FOR(i, 2, m) sum[i] += sum[i - 1];
ROF(i, n, 1) sa[sum[x[i]]--] = i;
for (j = 1; m < n; j <<= 1, m = k) {
k = 0;
FOR(i, n - j + 1, n) y[++k] = i;
FOR(i, 1, n) if (sa[i] > j) y[++k] = sa[i] - j;
memset(sum, 0, sizeof(array));
FOR(i, 1, n) ++sum[z[i] = x[y[i]]];
FOR(i, 2, m) sum[i] += sum[i - 1];
ROF(i, n, 1) sa[sum[z[i]]--] = y[i];
swap(x, y), x[sa[1]] = k = 1;
FOR(i, 2, n) {
int p = sa[i], q = sa[i - 1];
x[p] = y[p] == y[q] && y[p + j] == y[q + j] ? k : ++k;
}
}
memcpy(rank, x, sizeof(array));
FOR(i, 1, n) {
j = sa[rank[i] - 1];
z[i] = z[i - 1] ? z[i - 1] - 1 : 0;
for (; st[i + z[i]] == st[j + z[i]]; ++z[i]);
height[rank[i]] = z[i];
}
}
int back[200], L, ans;
array belong;
arr w, len, begin, end;
void init(int &n, int &m)
{
int i, j, k;
memset(back, 0, sizeof back);
scanf("%d\n", &n), m = n;
int *ptr = st;
FOR(i, 1, n) {
begin[i] = k = j = ptr - st + 1;
for (*(++ptr) = getchar(); 'a' <= *ptr && *ptr <= 'z'; *(++ptr) = getchar()) {
if (!back[*ptr]) back[*ptr] = ++m;
*ptr = back[*ptr];
belong[k] = i, ++k;
}
*ptr = i, belong[end[i] = ptr - st] = 0;
len[i] = ptr - st - j;
scanf("%d\n", w + i);
}
L = ptr - st;
}
namespace rmq
{
array mini[19];
void prepare()
{
int i, j;
int *p = mini[0], *q;
FOR(i, 1, L) p[i] = height[i];
FOR(i, 1, 18) {
q = p, p = mini[i];
FOR(j, 1, L) p[j] = min(q[j], q[j + (1 << (i - 1))]);
}
}
}
namespace tree
{
struct node
{
node *s, *t, *f;
int l, r, v;
};
node ns[maxn*2], *nt = ns, *root, *back[maxn];
int L, R, ans;
void build(node *&p, int l, int r)
{
p = ++nt, p->l = l, p->r = r, p->v = 0;
if (l == r) {back[l] = p; return;}
build(p->s, l, (l + r) >> 1);
build(p->t, ((l + r) >> 1) + 1, r);
p->s->f = p->t->f = p;
}
void init(int r) {nt = ns; build(root, 1, r);}
void insert(int pos, int k)
{
node *p = back[pos]; p->v = k;
for (p = p->f; p; p = p->f) p->v = max(p->s->v, p->t->v);
}
void query(node *p)
{
if (L <= p->l && p->r <= R) {ans = p->v; return;}
if (L <= p->s->r && p->s->v > ans) query(p->s);
if (p->t->l <= R && p->t->v > ans) query(p->t);
}
int query(int l, int r) {return ans = 0, L = l, R = r, query(root), ans;}
}
void work(int n)
{
int i, j, k, l, x, y; ans = 0;
rmq::prepare(), tree::init(L);
ROF(i, n, 1) {
int pos = rank[begin[i]], ll, rr;
l = len[i], ll = rr = pos, ++rr;
ROF(j, 19, 0) if ((x = rr + (1 << j) - 1) < L)
if (rmq::mini[j][rr] >= l) rr = x;
ROF(j, 19, 0) if ((x = ll - (1 << j) + 1) > 0)
if (rmq::mini[j][x] >= l) ll = x;
maxim(ans, k = tree::query(ll, rr) + w[i]);
x = begin[i], y = end[i];
for (j = x; j < y; ++j) tree::insert(rank[j], k);
}
printf("%d\n", ans);
}
int main()
{
#ifndef ONLINE_JUDGE
freopen("p3.in", "r", stdin);
freopen("p3.out", "w", stdout);
#endif
int T, n, m;
for (scanf("%d", &T); T--; ) {
init(n, m);
build(L, m);
work(n);
}
return 0;
}