HDU5790 Prefix (主席树 + 单词Trie)

题目链接: Prefix

大致题意

给出n个字符串, 编号从1~n.

每次询问编号在[l, r]区间的所有字符串中, 不同前缀的数目. (强制在线)

解题思路

思维

首先考虑到字符串的总长度为1E5, 因此不同前缀的数量最多为1E5.

如果不加以区间的限制, 询问所有的字符串有多少不同的前缀. 则我们可以通过Trie来完成. 我们把字符串插入Trie中, 如果此时我们为当前字符开辟了新节点, 则不同前缀数目+1.

考虑区间限制, 如果我们把不同的前缀 修改为 不同的数字(即: 询问[l, r]区间不同数字的数目), 则这个题变成了HH的项链, 只不过是强制在线了, 我们可以通过主席树来解决.


主席树 + Tire

我们考虑通过Trie树来存储字符串, 对于每个节点我们都存储上一次出现的位置.

这样用HH的项链中的解法三来维护区间不同数字的个数即可.

AC代码

#include <bits/stdc++.h>
#define rep(i, n) for (int i = 1; i <= (n); ++i)
using namespace std;
typedef long long ll;
const int N = 1E5 + 5, L = 0, R = N - 5;
int root[N], ind;
struct node {
	int l, r;
	int val;
}t[N * 18];
int build(int a, int c, int tl, int tr, int p) {
	int x = ++ind; t[x] = t[p];
	t[x].val += c;
	if (tl == tr) return x;
	int mid = tl + tr >> 1;
	if (a <= mid) t[x].l = build(a, c, tl, mid, t[p].l);
	else t[x].r = build(a, c, mid + 1, tr, t[p].r);
	return x;
}
int ask(int l, int r, int tl, int tr, int p, int x) {
	if (l <= tl and r >= tr) return t[x].val - t[p].val;
	int mid = tl + tr >> 1;
	int res = 0;
	if (l <= mid) res += ask(l, r, tl, mid, t[p].l, t[x].l);
	if (r > mid) res += ask(l, r, mid + 1, tr, t[p].r, t[x].r);
	return res;
}
void initct(int n) {
	rep(i, n) root[i] = 0;
	rep(i, ind) t[i] = { 0, 0, 0 };
	ind = 0;
}


namespace {
	int t[N][26], ind;
	int pos[N];
	void insert(int a, const string& s) {
		int x = 0; int pre = a - 1;
		for (auto& op : s) {
			char c = op - 'a';
			if (!t[x][c]) t[x][c] = ++ind;
			x = t[x][c];
			root[a] = build(pos[x], 1, L, R, root[pre]);
			pre = a; pos[x] = a;
		}
	}
	void clear(int x = 0) {
		for (int i = 0; i < 26; ++i) {
			if (t[x][i]) {
				pos[t[x][i]] = 0;
				clear(t[x][i]), t[x][i] = 0;
			}
		}
	}
	void inittrie() { ind = 0, clear(); }
}
char s[N];
int main()
{
	int n;
	while (~scanf("%d", &n)) {
		inittrie(), initct(n);

		rep(i, n) {
			scanf("%s", s);
			insert(i, s);
		}

		int m; scanf("%d", &m);
		int res = 0;
		rep(i, m) {
			int l, r; scanf("%d %d", &l, &r);
			l = (l + res) % n + 1, r = (r + res) % n + 1;
			if (l > r) swap(l, r);

			printf("%d\n", res = ask(0, l - 1, L, R, root[l - 1], root[r]));
		}
	}
    
	return 0;
}

END

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

逍遥Fau

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值