BZOJ 2780 SPOJ 8093 Sevenk Love Oimaster 后缀自动机+fenwick

题目大意:给出一些字符串,给出一些询问,每次问当前串在源串中的几个中出现过。


思路:将所有源串建立广义后缀自动机。每次新的一个串的时候,把last清成root,往里面加的时候,如果last指针往下走的时候已经有节点了,就需要拓展一个新的节点出来,否则就不满足广义后缀自动机的性质。此外,每一个节点代表的不一定是一个串,可能代表的是多个串的子串,所以要在每个点后面挂链,来表示这个节点是属于哪几个串中的子串。后面的事情就比较简单了,把后缀树建立出来,弄出DFS序,离线处所有询问,变成在一段序列中出现过多少不同的数字,弄一个树状数组维护一下。

其实和喵星球上的点名是差不多的,但是那个题我用的后缀数组暴力的,这个题会T。

还有这个题坑爹的卡内存啊,字符集太大,直接建立肯定mle,用map的话就不能用指针,我这还是第一次用数组写啊。。DT死了。。。


CODE:

//#define _CRT_SECURE_NO_DEPRECATE

#include <map>
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#define MAX 200010
using namespace std;

struct Graph{
	int head[MAX],total;
	int next[MAX],aim[MAX];

	void Add(int x,int y) {
		next[++total] = head[x];
		aim[total] = y;
		head[x] = total;
	}
}G,repre;

struct Ask{
	pair<int,int> interval;
	int id;

	Ask(pair<int,int> _,int __):interval(_),id(__) {}
	Ask() {}
	bool operator <(const Ask &a)const {
		return interval.second < a.interval.second;
	}
}ask[MAX];

int len[MAX],father[MAX];
map<int,int> tranc[MAX];
int cnt,root,last;

inline int NewNode(int _)
{
	len[++cnt] = _;
	return cnt;
}

void Initialize()
{
	root = last = NewNode(0);
}

inline void Add(int c,int i)
{
	int p = tranc[last][c];
	if(p) {
		if(len[p] == len[last] + 1)	last = p;
		else {
			int rep = NewNode(len[last] + 1);
			father[rep] = father[p];
			father[p] = rep;
			tranc[rep] = tranc[p];
			for(int temp = last; temp && tranc[temp][c] == p; temp = father[temp])
				tranc[temp][c] = rep;
			last = rep;
		}
	}
	else {
		int np = NewNode(len[last] + 1);
		for(p = last; p && !tranc[p][c]; p = father[p])
			tranc[p][c] = np;
		if(!p)	father[np] = root;
		else {
			int q = tranc[p][c];
			if(len[q] == len[p] + 1)	father[np] = q;
			else {
				int nq = NewNode(len[p] + 1);
				father[nq] = father[q];
				father[q] = father[np] = nq;
				tranc[nq] = tranc[q];
				for(; p && tranc[p][c] == q; p = father[p])
					tranc[p][c] = nq;
			}
		}
		last = np;
	}
	repre.Add(last,i);
}

pair<int,int> subtree[MAX];
int seq[MAX],_clock;

void DFS(int x)
{
	seq[++_clock] = x;
	subtree[x].first = _clock;
	for(int i = G.head[x]; i; i = G.next[i])
		DFS(G.aim[i]);
	subtree[x].second = _clock;
}

char s[MAX << 1];

inline pair<int,int> Find()
{
	int now = root,length = strlen(s);
	for(int i = 0; i < length; ++i) {
		if(!tranc[now][s[i]])	return make_pair(-1,-1);
		now = tranc[now][s[i]];
	}
	return subtree[now];
}

int fenwick[MAX];

inline void Fix(int x,int c)
{
	for(; x <= cnt; x += x&-x)
		fenwick[x] += c;
}

inline int GetSum(int x)
{
	int re = 0;
	for(; x; x -= x&-x)
		re += fenwick[x];
	return re;
}

int strings,asks;
int last_add[MAX],ans[MAX];

int main()
{
	Initialize();
	scanf("%d%d",&strings,&asks);
	for(int i = 1; i <= strings; ++i) {
		scanf("%s",s);
		int length = strlen(s);
		last = root;
		for(int j = 0; j < length; ++j)
			Add(s[j],i);
	}
	for(int i = 1; i <= cnt; ++i)
		if(father[i])
			G.Add(father[i],i);
	DFS(1);
	for(int i = 1; i <= asks; ++i) {
		scanf("%s",s);
		ask[i] = Ask(Find(),i);
	}
	sort(ask + 1,ask + asks + 1);
	
	int now = 1;
	while(ask[now].interval.first == -1 && now <= asks)	++now;
	for(int i = 1; i <= cnt; ++i) {
		for(int j = repre.head[seq[i]]; j; j = repre.next[j]) {
			int col = repre.aim[j];
			Fix(i,1);
			if(last_add[col])
				Fix(last_add[col],-1);
			last_add[col] = i;
		}
		for(; ask[now].interval.second == i; ++now)
			ans[ask[now].id] = GetSum(ask[now].interval.second) - GetSum(ask[now].interval.first - 1);
	}
	for(int i = 1; i <= asks; ++i)
		printf("%d\n",ans[i]);
	return 0;
}


你可能感兴趣的:(树状数组,后缀自动机,后缀树,bzoj)