【cf666e Forensic Examination】(后缀自动机+线段树合并)

10 篇文章 0 订阅
2 篇文章 0 订阅

这个题确实思维方向非常重要!

如果用后缀数组做,就转化为区间众数,一脸不可做。(当然莫对+堆还是可以的。。)

这个题应该用后缀自动机或者后缀树来做。这样问题就是子树众数,就可以用线段树合并一个log搞定。(为什么我一个log比别人一个log+一个根号还慢啊)

#include<cstdio>
#include<algorithm>
#include<iostream>
#include<cstring>
#include<vector>
#define rep(i,a,b) for(int i=a;i<=b;++i)
#define erp(i,a,b) for(int i=a;i>=b;--i)
#define LL long long
using namespace std;
const int MAXN = 500005;
const int MAXS = 1230000;
int N, M;

char s[MAXN];
int ch[MAXS][27], fa[MAXS], ml[MAXS], anc[21][MAXS], pos[MAXN], rt[MAXS], que[MAXS];
int tot = 1, last = 1;
inline void extend(int c)
{
	int p = last;
	int np = last = ++tot;
	ml[np] = ml[p] + 1;
	while (p&&!ch[p][c]) ch[p][c] = np, p = fa[p];
	if (!p) { fa[np] = 1; return; }
	int q = ch[p][c];
	if (ml[p]+1 == ml[q]) fa[np] = q;
	else {
		int nq = ++tot; ml[nq] = ml[p]+1;
		memcpy(ch[nq], ch[q], sizeof ch[q]);
		fa[nq] = fa[q];
		fa[np] = fa[q] = nq;
		while (ch[p][c]==q) ch[p][c] = nq, p = fa[p];
	}
}
inline int getpos(int l, int r)
{
	int len = r-l+1, p = pos[r];
	erp(i,20,0) if(ml[anc[i][p]] >= len) p = anc[i][p];
	return p;
}
void toposort()
{
	static int ws[MAXS];
	rep(i, 1, tot) ws[ml[i]] ++;
	rep(i, 1, tot) ws[i] += ws[i-1];
	rep(i, 1, tot) que[ws[ml[i]]--] = i;
}

int ncnt;
#define lch(x) tr[x].lch
#define rch(x) tr[x].rch
struct info { int mx, id; };
struct Node {
	info t;
	int lch, rch;
} tr[MAXS*22];
info operator | (const info&a, const info&b)
{
	if (a.mx > b.mx) return a;
	if (a.mx < b.mx) return b;
	return a.id < b.id ? a : b;
}
void ins(int&p, int v, int l=1, int r=M)
{
	if (!p) p = ++ncnt;
	if (l==r) { tr[p].t.mx ++, tr[p].t.id = l; return; }
	int mid = (l+r)>>1;
	if (v<=mid) ins(lch(p), v, l, mid);
	else ins(rch(p), v, mid+1, r);
	tr[p].t = tr[lch(p)].t | tr[rch(p)].t;
}
int Merge(int x, int y, int l=1, int r=M)
{
	if (!x||!y) return x|y;	
	int z = ++ncnt;
	if (l==r) {
		tr[z].t = (info){tr[x].t.mx+tr[y].t.mx, l};
		return z;
	}
	int mid = (l+r)>>1;
	lch(z) = Merge(lch(x), lch(y), l, mid);
	rch(z) = Merge(rch(x), rch(y), mid+1, r);
	tr[z].t = tr[lch(z)].t | tr[rch(z)].t;
	return z;
}
info ask(int p, int L, int R, int l=1, int r=M)
{
	if (!p) return (info){0,0};
	if (l>=L && r<=R) return tr[p].t;
	int mid = (l+r)>>1;
	if (R<=mid) return ask(lch(p), L, R, l, mid);
	if (L>mid) return ask(rch(p), L, R, mid+1, r);
	return ask(lch(p), L, R, l, mid) | ask(rch(p), L, R, mid+1, r);
}

void init()
{
	toposort();
	erp(i,tot,1)
	{
		int u = que[i];
		if (fa[u]) rt[fa[u]] = Merge(rt[fa[u]], rt[u]);
	}
	rep(i,1,tot) anc[0][i] = fa[i];
	rep(i,1,20) rep(j,1,tot) anc[i][j] = anc[i-1][ anc[i-1][j] ];
}

int main()
{
	scanf("%s", s+1);
	N = strlen(s+1);
	rep(i,1,N) extend(s[i]-'a'), pos[i] = last;
	extend(26);
	scanf("%d", &M);
	rep(i, 1, M)
	{
		scanf("%s", s+1);
		for (int j = 1; s[j]; ++j)
			extend(s[j]-'a'), ins(rt[last], i);
		extend(26);
	}
	init();
	int Q, a, b, l, r, p;
	scanf("%d", &Q);
	while (Q--)
	{
		scanf("%d%d%d%d", &a, &b, &l, &r);
		p = getpos(l, r);
		info tmp = ask(rt[p], a, b);
		if (tmp.mx == 0) printf("%d 0\n", a);
		else printf("%d %d\n", tmp.id, tmp.mx);
	}
	return 0;
}


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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值