傳送到Codeforces:(/ω\)……… (/ω•\)ui
很水的一道題。
對查詢的串建出來AC自動機,而後樹上隨便跑跑就好了。
爲何要寫這篇題解呢?
我第一眼看到這個題:「哈哈,有根樹上的路徑信息查詢,點分治就行了,被我秒啦!」
「這個題好像是某Qualification Round的題啊。。。怎麼Qual就出點分治啊,真毒瘤。」
而後碼碼碼。。。
「怎麼TLE了,卡卡常。」
卡常ing。。。
「怎麼又WA了,難道卡哈希?毒瘤毒瘤。」
debugging。。。
「算了調不出來了拿tourist的代碼拍一下吧。」
「這場比賽怎麼AK了三百我的啊?這題怎麼代碼這麼短啊?」
「。。。彷佛KMP而後dfs一下就沒了。。。我簡直藥丸。。。」
而後事實證實KMP的複雜度是錯的,很容易就卡掉了,卡的方法也很簡單,你們本身想一想吧。。。
若是用KMP的話是TLE on test 30,若是是比賽就直接FST了。。。
因此用AC自動機就沒問題了喵~spa
#include <cstring> #include <algorithm> #include <cstdio> #include <queue> using namespace std; const int MAXN = 400010; int _w; int n, ch[MAXN], m; char str[MAXN]; namespace G { int head[MAXN], nxt[MAXN], to[MAXN], eid; void init() { eid = 0; memset(head, -1, sizeof head); } void adde( int u, int v ) { to[eid] = v, nxt[eid] = head[u], head[u] = eid++; } } void add_edge( int u, int v, char *str ) { for( char *p = str; *p; ++p ) { ch[++m] = *p - 'a'; if( p == str ) G::adde(u, m); else G::adde(m-1, m); } G::adde(m, v); } namespace AC { int ch[MAXN][26], nid; bool word[MAXN]; queue<int> q; int f[MAXN]; void insert( char *s ) { int u = 0; for( ; *s; ++s ) { int c = *s - 'a'; if( !ch[u][c] ) ch[u][c] = ++nid; u = ch[u][c]; } word[u] = 1; } void build() { for( int i = 0; i < 26; ++i ) if( ch[0][i] ) q.push( ch[0][i] ); while( !q.empty() ) { int u = q.front(); q.pop(); for( int i = 0; i < 26; ++i ) { int v = ch[u][i]; if( !v ) { ch[u][i] = ch[f[u]][i]; continue; } int w = f[u]; while( w && !ch[w][i] ) w = f[w]; f[v] = ch[w][i]; q.push(v); } } } } int ans; void solve( int u, int o ) { using namespace G; if( ch[u] != -1 ) { o = AC::ch[o][ch[u]]; if( AC::word[o] ) ++ans; } for( int i = head[u]; ~i; i = nxt[i] ) solve( to[i], o ); } int main() { _w = scanf( "%d", &n ); m = n; G::init(); for( int i = 2; i <= n; ++i ) { int pa; _w = scanf( "%d%s", &pa, str ); add_edge(pa, i, str); } for( int i = 1; i <= n; ++i ) ch[i] = -1; _w = scanf( "%s", str ); AC::insert(str); AC::build(); solve(1, 0); printf( "%d\n", ans ); return 0; }