【SPOJ 8093】Sevenk Love Oimaster

http://www.spoj.com/problems/JZPGYZ/
查询一个询问串在上面n个串中多少个串的子串。
后缀数组+主席树,常熟有点大。。。
建出广义SAM,利用parent树的dfs序,每次相当于询问parent树的一个子树中出现了多少不同的颜色。
可以用主席树统计,pre表示dfs序上与这个位置属于同一个串的前一个位置在哪,主席树询问区间内的pre小于这个区间左端点的个数(也就是不同的颜色数)。
或者更方便地,把每个询问拆成parent树dfs序上的两个前缀和相减。
离线排序前缀和从左到右用bits维护pre为权值的树状数组并且查询小于某个区间左端点的pre的个数,统计答案时做一下减法就可以了(类似主席树)。
时间复杂度\(O(n\log n)\)
还有不科学的暴力的做法,对于n个模板串,暴力在parent树上打标记,时间复杂度\(O(n^2)\)
数据并没有卡这种做法。于是我本着复习广义后缀自动机的原则(和懒得写bits的原则),只写了这种暴力的做法。。。
将近花了一天重新脑补了一下广义SAM,感觉以前直接套用普通SAM的插入模板导致多出来了一些点不美观(当然多出来的这些点都可以合并到它们的parent),就重写了一下不会多出来点的广义SAM模板,常数更大,代码更长qwq,但保证所有节点的Right集合都是它们parent的Right集合的真子集(及不用费心想着哪些点可以合并在一起了)。

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;

const int N = 360003;

char s[N];
int l[N], r[N];

struct State {
	State *par, *go[50];
	int val, tim, cnt;
	State(int _num) : val(_num) {
		par = 0; tim = cnt = 0;
		memset(go, 0, sizeof(go));
	}
} *root, *last;

void extend(int w) {
	if (last->go[w] && last->go[w]->val == last->val + 1) {last = last->go[w]; return;}
	State *p = last;
	State *np = new State(p->val + 1);
	while (p && p->go[w] == 0)
		p->go[w] = np, p = p->par;
	if (p == 0) np->par = root;
	else {
		State *q = p->go[w];
		if (q->val == p->val + 1) np->par = q;
		else if (p != last) {
				State *nq = new State(p->val + 1);
				memcpy(nq->go, q->go, sizeof(q->go));
				nq->par = q->par; q->par = np->par = nq;
				while (p && p->go[w] == q)
					p->go[w] = nq, p = p->par;
			} else {
				memcpy(np->go, q->go, sizeof(q->go));
				np->par = q->par; q->par = np;
				while (p && p->go[w] == q)
					p->go[w] = np, p = p->par;
			}
	}
	last = np;
}

void mark(State *t, int timing) {
	if (t == root || t->tim == timing) return;
	t->tim = timing; ++t->cnt;
	mark(t->par, timing);
}

int n, m;

int main() {
	root = new State(0);
	scanf("%d%d", &n, &m);
	for (int i = 1; i <= n; ++i) {
		l[i] = r[i - 1] + 1;
		scanf("%s", s + l[i]);
		r[i] = l[i] + strlen(s + l[i]) - 1;
	}
	
	for (int i = 1; i <= n; ++i) {
		last = root;
		for (int j = l[i], top = r[i]; j <= top; ++j)
			extend(s[j] - 'a');
	}
	
	State *tmp;
	for (int i = 1; i <= n; ++i) {
		tmp = root;
		for (int j = l[i], top = r[i]; j <= top; ++j) {
			tmp = tmp->go[s[j] - 'a'];
			mark(tmp, i);
		}
	}
	
	int len; bool flag;
	for (int i = 1; i <= m; ++i) {
		scanf("%s", s);
		len = strlen(s);
		tmp = root; flag = true;
		for (int j = 0; j < len; ++j)
			if (tmp->go[s[j] - 'a'])
				tmp = tmp->go[s[j] - 'a'];
			else {
				flag = false;
				break;
			}
		if (!flag) puts("0");
		else printf("%d\n", tmp->cnt);
	}
	return 0;
}
posted @ 2017-03-31 20:44  abclzr  阅读(276)  评论(0编辑  收藏  举报