【HDU】5069 Harry And Biological Teacher AC自动机fail指针建树dfs

传送门:【HDU】5069 Harry And Biological Teacher


先扯淡:西安区域赛运气好拿了个银回来,但然后就是无尽的补作业+补题,生活如此艰难,何必非要拆穿TUT。。

做这道题是有一个故事的:去西安的火车上数一和我提起鞍山的L题,我想了想就觉得和AC自动机的fail指针有关(fail指针建树什么的),但是短时间内没啥思路。。就放着了,队友听到我讨论那题就和我说了一个类似的题(简单很多的,也就是这题),仔细瞅瞅还是可以写的,于是就敲掉了,中间由于算错空间复杂度害我一度把这道胡搞题想成神题= =。。。(10^5个a形成的串和10^5-1个a形成的串和在一起我竟然算2*10^5。。。我也是厉害= =。。后来反应过来就发现可以随便做了。。)

题目分析:首先我们先用所有的串构造一个AC自动机(不知道?没关系,看完下面一定会懂的^_^),然后我们就可以得到AC自动机的fail指针(如果存在一个结点u的fail指针指向结点v,那么我们可以认为以u为结尾的串的最长后缀所匹配的串的最长前缀的结尾为v。。可能很绕。。。根据fail指针的性质容易知道一个结点u的fail指针指向的结点v所构成的前缀正好是结点u的后缀之一(也是最长的前后缀匹配))。然后我们用fail指针的反向形式构建fail指针树,那么每个结点v的父节点u就是以结点v为终点的最长匹配前缀的终点(不理解可以慢慢理解。。。首先要对AC自动机的一些性质有掌握。。。)。那么我们以dfs为基础,当我们遍历到一个结点时,就将经过该结点的所有串添加到set里面去,每个串为一个set,set里面保存该串形成的最长长度,然后我们查询以该结点为终点的所有询问(忘记说了。。询问神马的要先离线处理好),通过查找对应串的set中的最大元素求解。然后继续dfs,当该结点的子树都dfs完以后,将原先在该结点上插入的值全部删除。

可能讲的有点混乱。。。

Orz。。。只会做不会描述。。。。

辛苦读这题解的各位了。。。希望大家能从中有收获~


代码如下:


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

#define rep( i , a , b ) for ( int i = a ; i < b ; ++ i )
#define For( i , a , b ) for ( int i = a ; i <= b ; ++ i )
#define rev( i , a , b ) for ( int i = a ; i >= b ; -- i )
#define clr( a , x ) memset ( a , x , sizeof a )

const int MAXN = 100005 ;
const int MAXE = 100005 ;

struct Edge {
	int v , n ;
	Edge () {}
	Edge ( int v , int n ) : v ( v ) , n ( n ) {}
} ;

struct Node {
	int v , d , n ;
	Node () {}
	Node ( int v , int d , int n ) : v ( v ) , d ( d ) , n ( n ) {}
} ;

struct Query {
	int v , idx , n ;
	Query () {}
	Query ( int v , int idx , int n ) : v ( v ) , idx ( idx ) , n ( n ) {}
} ;

Query Q[MAXE] ;
Edge E[MAXE] ;
Node N[MAXE] ;
int query[MAXN] , cntQ ;
int H[MAXN] , cntE ;
int node[MAXN] , cntN ;
int word[MAXN] ;
int ans[MAXN] ;
set < int > S[MAXN] ;
int n , m ;

void clear () {
	cntN = cntE = cntQ = 0 ;
	clr ( H , -1 ) ;
	clr ( node , -1 ) ;
	clr ( query , -1 ) ;
}

void addedge ( int u , int v ) {
	E[cntE] = Edge ( v , H[u] ) ;
	H[u] = cntE ++ ;
}

void naddedge ( int u , int v , int d ) {
	N[cntN] = Node ( v , d , node[u] ) ;
	node[u] = cntN ++ ;
}

void qaddedge ( int u , int v , int idx ) {
	Q[cntQ] = Query ( v , idx , query[u] ) ;
	query[u] = cntQ ++ ;
}

struct AC_automation {
	int next[MAXN][4] ;
	int fail[MAXN] ;
	int P ;
	int root ;
	int head , tail ;
	int Q[MAXN] ;
	
	int newnode () {
		rep ( i , 0 , 4 ) next[P][i] = -1 ;
		return P ++ ;
	}
	
	void init () {
		P = 0 ;
		root = newnode () ;
	}
	
	int get ( char c ) {
		if ( c == 'A' ) return 0 ;
		if ( c == 'C' ) return 1 ;
		if ( c == 'G' ) return 2 ;
		if ( c == 'T' ) return 3 ;
	}
	
	void insert ( char buf[] , int idx ) {
		int now = root , d = 0 ;
		for ( int i = 0 ; buf[i] ; ++ i ) {
			int index = get ( buf[i] ) ;
			if ( next[now][index] == -1 ) next[now][index] = newnode () ;
			now = next[now][index] ;
			++ d ;
			naddedge ( now , idx , d ) ;
		}
		word[idx] = now ;
	}
	
	void build () {
		head = tail = 0 ;
		fail[root] = root ;
		rep ( i , 0 , 4 ) {
			if ( next[root][i] == -1 ) next[root][i] = root ;
			else {
				fail[next[root][i]] = root ;
				Q[tail ++] = next[root][i] ;
			}
		}
		while ( head != tail ) {
			int now = Q[head ++] ;
			rep ( i , 0 , 4 ) {
				if ( ~next[now][i] ) {
					fail[next[now][i]] = next[fail[now]][i] ;
					Q[tail ++] = next[now][i] ;
				} else next[now][i] = next[fail[now]][i] ;
			}
		}
	}
} ac ;

void dfs ( int u ) {
	for ( int i = node[u] ; ~i ; i = N[i].n ) S[N[i].v].insert ( N[i].d ) ;//insert
	for ( int i = query[u] ; ~i ; i = Q[i].n ) {//get ans
		int v = Q[i].v ;
		int idx = Q[i].idx ;
		if ( S[v].empty () ) ans[idx] = 0 ;
		else ans[idx] = *( -- S[v].end () ) ;
	}
	for ( int i = H[u] ; ~i ; i = E[i].n ) dfs ( E[i].v ) ;//dfs
	for ( int i = node[u] ; ~i ; i = N[i].n ) S[N[i].v].erase ( N[i].d ) ;//erase
}

char buf[MAXN] ;

void solve () {
	int u , v ;
	ac.init () ;
	clear () ;
	For ( i , 1 , n ) S[i].clear () ;
	For ( i , 1 , n ) {
		scanf ( "%s" , buf ) ;
		ac.insert ( buf , i ) ;
	}
	ac.build () ;
	rep ( i , 1 , ac.P ) addedge ( ac.fail[i] , i ) ;
	rep ( i , 0 , m ) {
		scanf ( "%d%d" , &u , &v ) ;
		qaddedge ( word[u] , v , i ) ;
	}
	dfs ( ac.root ) ;
	rep ( i , 0 , m ) printf ( "%d\n" , ans[i] ) ;
}

int main () {
	while ( ~scanf ( "%d%d" , &n , &m ) ) solve () ;
	return 0 ;
}

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值