离线建AC自动机维护子串+线段树维护AC自动机:HDU4117

https://acm.hdu.edu.cn/showproblem.php?pid=4117

离线处理

AC自动机每次插入都要重构,但其实可以先离线建好,再进行操作

AC自动机理解——维护子串

每个子串都可以表示成一个前缀的一个后缀。

任意一个前缀是Trie树上的一个点,然后其对应后缀就是fail树上的祖先

fail树本质是一个后缀树

线段树维护

现在在fail树上操作,对每个点查询all祖先,可以变成祖先修改时对其所有儿子进行修改、

维护出每个点的dfs序,然后拿个线段树维护即可

#include 
#include 
#include
using namespace std;
//#define int long long
inline int read(){int x=0,f=1;char ch=getchar(); while(ch<'0'||
ch>'9'){if(ch=='-')f=-1;ch=getchar();}while(ch>='0'&&ch<='9'){
x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}return x*f;}
#define Z(x) (x)*(x)
#define pb push_back
//mt19937 rand(time(0));
//mt19937_64 rand(time(0));
//srand(time(0));
#define N 20010
#define M 300010
//#define mo
int n, m, i, j, k, T, T0;
int nxt[M][26], fail[M], tot; 
short dfn[M], len[M], lst[M], p[M]; 
int rt, u, v, w[N], ans, f; 
char str[M]; 
int q[M], l, r; 
vector<int>G[M]; 

int mx[N<<3]; 
void add(int k, int l, int r, int x, int y, int z) {
	if(l>=x && r<=y) return mx[k]=max(mx[k], z), void(); 
	int m=(l+r)>>1; 
	if(x<=m) add(k<<1, l, m, x, y, z); 
	if(y>=m+1) add(k<<1|1, m+1, r, x, y, z); 
}
int que(int k, int l, int r, int x) {
	if(l==r) return mx[k]; 
	int m=(l+r)>>1; 
	if(x<=m) return max(mx[k], que(k<<1, l, m, x)); 
	else return max(mx[k], que(k<<1|1, m+1, r, x)); 
}
void clear(int k, int l, int r) {
	mx[k]=0; 
	if(l==r) return ; 
	int m=(l+r)>>1; 
	clear(k<<1, l, m); clear(k<<1|1, m+1, r); 
}

void init() {
	rt=u=v=tot=ans=l=r=0; 
}

void Trie(int u, int i) {
	if(!str[i]) return dfn[u]=lst[u]=1, void(); 
	int c=str[i]-'a'; p[u]=1; 
	if(!nxt[u][c]) nxt[u][c]=++tot; 
	Trie(nxt[u][c], i+1); 
}

void bfs() {
	memset(fail, 0, sizeof(fail)); 
	q[++r]=1; fail[1]=0; 
	while(l<r) {
		u=q[++l]; 
		if(!p[u]) continue; 
		for(int i=0; i<26; ++i) 
			if(nxt[u][i]) {
				v=nxt[u][i]; k=fail[u]; 
				while(k && p[k] && !nxt[k][i]) k=fail[k]; 
				if(p[k] && nxt[k][i]) fail[v]=nxt[k][i]; 
				else fail[v]=1; 
				q[++r]=v; 
			}
	}
}

void dfs(int x) {
	if(dfn[x]) ++m; dfn[x]=m; 
	for(int y : G[x]) dfs(y); 
	if(lst[x]) lst[x]=m, ++m; 
	else lst[x]=m; 
}

signed main()
{
//	freopen("in.txt", "r", stdin);
//	freopen("out.txt", "w", stdout);
	T=read();
	for(T0=1; T0<=T; ++T0) {
		init(); 
		n=read(); rt=tot=1; 
		for(i=1; i<=n; ++i) {
			scanf("%s", str+len[i-1]+1); w[i]=read(); 
			len[i]=strlen(str+len[i-1]+1)+len[i-1]; 
			Trie(rt, len[i-1]+1); 
		}
		bfs(); 
		for(i=2; i<=tot; ++i) G[fail[i]].pb(i); 
		m=1; dfs(1);  
		for(i=1; i<=n; ++i) {
			f=w[i]; 
			for(j=len[i-1]+1, u=1; j<=len[i]; ++j) {
				u=nxt[u][str[j]-'a']; 
				f=max(f, que(1, 1, m, dfn[u])+w[i]); 
			}
			ans=max(ans, f); add(1, 1, m, dfn[u], lst[u], f); 			
		}
		clear(1, 1, m); 
		for(i=1; i<=tot; ++i) {
			if(p[i]) memset(nxt[i], 0, sizeof(nxt[i]));
			fail[i]=dfn[i]=lst[i]=p[i]=0; G[i].clear(); 
		}
		printf("Case #%d: %d\n", T0, ans); 
	}

	return 0;
}


你可能感兴趣的:(AC自动机,线段树)