【題解】Tree-String Problem Codeforces 291E AC自動機

Prelude

傳送到Codeforces:(/ω\)……… (/ω•\)ui


Solution

很水的一道題。
對查詢的串建出來AC自動機,而後樹上隨便跑跑就好了。
爲何要寫這篇題解呢?
我第一眼看到這個題:「哈哈,有根樹上的路徑信息查詢,點分治就行了,被我秒啦!」
「這個題好像是某Qualification Round的題啊。。。怎麼Qual就出點分治啊,真毒瘤。」
而後碼碼碼。。。
「怎麼TLE了,卡卡常。」
卡常ing。。。
「怎麼又WA了,難道卡哈希?毒瘤毒瘤。」
debugging。。。
「算了調不出來了拿tourist的代碼拍一下吧。」
「這場比賽怎麼AK了三百我的啊?這題怎麼代碼這麼短啊?」
「。。。彷佛KMP而後dfs一下就沒了。。。我簡直藥丸。。。」
而後事實證實KMP的複雜度是錯的,很容易就卡掉了,卡的方法也很簡單,你們本身想一想吧。。。
若是用KMP的話是TLE on test 30,若是是比賽就直接FST了。。。
因此用AC自動機就沒問題了喵~spa


Code

#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;
}
相關文章
相關標籤/搜索