【LOJ3049】「十二省联考 2019」字符串问题

博客介绍在后缀树上建边跑最长路的思路,给出了该方法的时间复杂度为 O(NaLog∣S∣+NbLog∣S∣+∣S∣Log∣S∣+M) ,还提供了题目链接。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

【题目链接】

【思路要点】

  • 直接在后缀树上建边跑最长路就行了。
  • 时间复杂度 O ( N a L o g ∣ S ∣ + N b L o g ∣ S ∣ + ∣ S ∣ L o g ∣ S ∣ + M ) O(N_aLog|S|+N_bLog|S|+|S|Log|S|+M) O(NaLogS+NbLogS+SLogS+M)

【代码】

#include<bits/stdc++.h>
const int MAXN = 4e5 + 5;
const int MAXM = 1e6 + 5;
const int MAXLOG = 20;
const long long INF = 1e18;
using namespace std;
template <typename T> void read(T &x) {
	x = 0; int f = 1;
	char ch = getchar();
	for (; !isdigit(ch); ch = getchar()) if (ch == '-') f = -f;
	for (; isdigit(ch); ch = getchar()) x = x * 10 + ch - '0';
	x *= f;
}
int val[MAXM];
bool cmp(pair <int, int> x, pair <int, int> y) {
	if (x.first == y.first) return val[x.second] < val[y.second];
	else return x.first < y.first;
}
struct SuffixAutomaton {
	struct Node {
		int child[26];
		int father, depth;
	} a[MAXN];
	int root, size, last, pos[MAXN];
	int father[MAXN][MAXLOG];
	int newnode(int depth) {
		a[size].depth = depth, a[size].father = 0;
		memset(a[size].child, 0, sizeof(a[size].child));
		return size++;
	}
	void extend(int ch, int from) {
		int p = last, np = newnode(a[p].depth + 1);
		while (a[p].child[ch] == 0) {
			a[p].child[ch] = np;
			p = a[p].father;
		}
		if (a[p].child[ch] == np) a[np].father = 0;
		else {
			int q = a[p].child[ch];
			if (a[p].depth + 1 == a[q].depth) a[np].father = q;
			else {
				int nq = newnode(a[p].depth + 1);
				memcpy(a[nq].child, a[q].child, sizeof(a[q].child));
				a[nq].father = a[q].father;
				a[q].father = a[np].father = nq;
				while (a[p].child[ch] == q) {
					a[p].child[ch] = nq;
					p = a[p].father;
				}
			}
		}
		pos[from] = last = np;
	}
	void init(char *s) {
		size = 0;
		root = last = newnode(0);
		int len = strlen(s + 1);
		for (int i = len; i >= 1; i--)
			extend(s[i] - 'a', i);
		static int cnt[MAXN];
		for (int i = 1; i <= len; i++)
			cnt[i] = 0;
		for (int i = 1; i <= size - 1; i++)
			cnt[a[i].depth]++;
		for (int i = 1; i <= len; i++)
			cnt[i] += cnt[i - 1];
		static int sa[MAXN];
		for (int i = size - 1; i >= 1; i--)
			sa[cnt[a[i].depth]--] = i;
		for (int i = 1; i <= size - 1; i++) {
			int pos = sa[i];
			memset(father[pos], 0, sizeof(father[pos]));
			father[pos][0] = a[pos].father;
			for (int p = 1; father[pos][p - 1] != 0; p++)
				father[pos][p] = father[father[pos][p - 1]][p - 1];
		}
	}
	vector <int> b[MAXM];
	vector <pair <int, int> > p[MAXN];
	int sp[MAXN], tp[MAXN]; bool instack[MAXM];
	long long dp[MAXM], ans;
	int jump(int pos, int depth) {
		for (int i = MAXLOG - 1; i >= 0; i--)
			if (a[father[pos][i]].depth >= depth) pos = father[pos][i];
		return pos;
	}
	long long getdp(int pos) {
		if (instack[pos]) {
			ans = INF;
			return INF;
		}
		if (dp[pos] != -1) return dp[pos];
		instack[pos] = true;
		long long res = 0;
		for (unsigned i = 0; i < b[pos].size(); i++)
			res = max(res, getdp(b[pos][i]));
		dp[pos] = res + val[pos];
		instack[pos] = false;
		if (ans == INF) return INF;
		else return dp[pos];
	}
	void getans() {
		int osize = size;
		for (int i = 0; i <= size - 1; i++)
			p[i].clear(), val[i] = 0;
		int n; read(n);
		for (int i = 1; i <= n; i++) {
			int x, y; read(x), read(y);
			int tmp = jump(pos[x], y - x + 1);
			sp[i] = size, val[size] = y - x + 1;
			p[tmp].push_back(make_pair(y - x + 1, size++));
		}
		int m; read(m);
		for (int i = 1; i <= m; i++) {
			int x, y; read(x), read(y);
			int tmp = jump(pos[x], y - x + 1);
			tp[i] = size, val[size] = 0;
			p[tmp].push_back(make_pair(y - x + 1, size++));
		}
		for (int i = 0; i <= size + n - 1; i++) {
			b[i].clear(); dp[i] = -1;
			instack[i] = false;
		}
		int q; read(q);
		for (int i = 1; i <= q; i++) {
			int x, y; read(x), read(y);
			b[sp[x]].push_back(tp[y]);
		}
		for (int i = 1; i <= osize - 1; i++) {
			sort(p[i].begin(), p[i].end(), cmp);
			if (p[i].empty()) b[a[i].father].push_back(i);
			else {
				for (unsigned j = 0; j < p[i].size(); j++) {
					if (val[p[i][j].second]) {
						val[size] = 0;
						b[size].push_back(p[i][j].second);
						p[i][j].second = size++;
					}
				}
				b[a[i].father].push_back(p[i][0].second);
				for (unsigned j = 1; j < p[i].size(); j++)
					b[p[i][j - 1].second].push_back(p[i][j].second);
				b[p[i].back().second].push_back(i);
			}
		}
		ans = 0;
		for (int i = 1; i <= n; i++)
			ans = max(ans, getdp(sp[i]));
		if (ans >= INF) printf("-1\n");
		else cout << ans << endl;
	}
} SAM;
char s[MAXN];
int main() {
	int T; read(T);
	while (T--) {
		scanf("\n%s", s + 1);
		SAM.init(s);
		SAM.getans();
	}
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值