bzoj 2434 ac自动机

 

ac自动机中,如果以trie中的节点为节点,(fail[i],i)为边,可以建立一颗树,该树有如下特点:“节点u是节点v的祖先 当且仅当 u代表的字符串是v代表的字符串的一个后缀”。(u代表的字符串是由根节点到u路径上所有的边代表的字符顺次组合成的,我们记作str(u))。

 

本题中的每一个P都对应trie中的一个节点,所以本题就是求str(b)中有多少个str(a)子串:

如果len(str(b))<len(str(a)),则为0

如果len(str(b))==len(str(a)),则判断a和b是否是同一个字符串。

如果len(str(b))<len(str(a)),则str(a)一定是str(b)一个前缀的后缀,str(b)的前缀就是根到b路径上所有点代表的字符串,而如果str(a)是str(b)的一个后缀,则在fail树中,b一定在a的子树中。

  1 /**************************************************************
  2     Problem: 2434
  3     User: idy002
  4     Language: C++
  5     Result: Accepted
  6     Time:580 ms
  7     Memory:18912 kb
  8 ****************************************************************/
  9  
 10 #include <cstdio>
 11 #include <cstring>
 12 #include <queue>
 13 #define maxn 100010
 14 #define fprintf(...)
 15 using namespace std;
 16  
 17 struct Query {
 18     int a, id;
 19     Query( int a, int id ):a(a),id(id){}
 20 };
 21  
 22 int n, m, ans[maxn];
 23 char str[maxn];
 24 int son[maxn][26], pre[maxn], fail[maxn], ppos[maxn], ntot;
 25 int ww[maxn], dl[maxn], dr[maxn], dfs_clock;
 26 vector<int> g[maxn];
 27 vector<Query> qry[maxn];
 28  
 29  
 30 void modify( int pos, int delta ) {
 31     for( int i=pos; i<=n; i+=i&-i )
 32         ww[i] += delta;
 33 }
 34 int query( int pos ) {
 35     int rt=0;
 36     for( int i=pos; i; i-=i&-i )
 37         rt += ww[i];
 38     return rt;
 39 }
 40 void build_trie( int n, const char *P ) {
 41     int u = 0;
 42     int pid_clock=0;
 43     for( int i=0; i<n; i++ ) {
 44         if( P[i]=='P' ) {
 45             pid_clock++;
 46             ppos[pid_clock] = u;
 47             fprintf( stderr, "the No.%d P is at node %d\n", pid_clock, u );
 48         } else if( P[i]=='B' ) {
 49             u = pre[u];
 50             fprintf( stderr, "up to %d\n", u );
 51         } else {
 52             int c=P[i]-'a';
 53             if( !son[u][c] ) {
 54                 ntot++;
 55                 son[u][c] = ntot;
 56                 pre[ntot] = u;
 57             }
 58             fprintf( stderr, "%d->%d with %c\n", u, son[u][c], c+'a' );
 59             u = son[u][c];
 60         }
 61     }
 62 }
 63 void build_fail() {
 64     queue<int> qu;
 65     for( int c=0; c<26; c++ ) {
 66         int v=son[0][c];
 67         if( !v ) continue;
 68         fail[v] = 0;
 69         fprintf( stderr, "fail[%d] = %d\n", v, fail[v] );
 70         g[0].push_back(v);
 71         qu.push( v );
 72     }
 73     while( !qu.empty() ) {
 74         int u=qu.front();
 75         qu.pop();
 76         for( int c=0; c<26; c++ ) {
 77             int v=son[u][c];
 78             int w=fail[u];
 79             if( !v ) continue;
 80             while( w && !son[w][c] ) w=fail[w];
 81             fail[v] = son[w][c];
 82             fprintf( stderr, "fail[%d] = %d\n", v, fail[v] );
 83             g[son[w][c]].push_back( v );
 84             qu.push( v );
 85         }
 86     }
 87 }
 88 void dfs( int u ) {
 89     dl[u] = ++dfs_clock;
 90     fprintf( stderr, "(%d ", u );
 91     for( int t=0; t<g[u].size(); t++ )
 92         dfs( g[u][t] );
 93     dr[u] = dfs_clock;
 94     fprintf( stderr, ")" );
 95 }
 96 void solve( int n, const char *P ) {
 97     int u=0;
 98     for( int i=0; i<n; i++ ) {
 99         if( P[i]=='P' ) {
100             for( int t=0; t<qry[u].size(); t++ ) {
101                 Query &q = qry[u][t];
102                 if( q.a==u ) ans[q.id]=1;
103                 else ans[q.id] = query(dr[q.a])-query(dl[q.a]-1);
104             }
105         } else if( P[i]=='B' ) {
106             if( u==0 ) continue;
107             modify( dl[u], -1 );
108             u = pre[u];
109         } else {
110             int c=P[i]-'a';
111             u = son[u][c];
112             modify( dl[u], +1 );
113         }
114     }
115 }
116 int main() {
117     scanf( "%s", str );
118     n = strlen(str);
119     build_trie(n,str);
120     build_fail();
121     dfs(0);
122     fprintf( stderr, "\n" );
123     scanf( "%d", &m );
124     for( int i=1,a,b; i<=m; i++ ) {
125         scanf( "%d%d", &a, &b );
126         a = ppos[a];
127         b = ppos[b];
128         qry[b].push_back( Query(a,i) );
129     }
130     solve(n,str);
131     for( int i=1; i<=m; i++ )
132         printf( "%d\n", ans[i] );
133 }
134     
View Code

收获:

1、“a是b的子串 当且仅当 a是b的后缀的前缀或前缀的后缀“ 所以统计时就可以根据这个分类判断。

2、 fail指针对应的树的含义:同一条链上,深度小的字符串是深度大的字符串的后缀。

 

 

posted @ 2015-03-14 16:15  idy002  阅读(137)  评论(0编辑  收藏  举报