[The 2011 ACM-ICPC ACRC]G.GRE Words

补充一下:这个东西貌似叫做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;
}




评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值