传送门:【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 ;
}