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