• 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
    ", pid_clock, u );
     48         } else if( P[i]=='B' ) {
     49             u = pre[u];
     50             fprintf( stderr, "up to %d
    ", 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
    ", 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
    ", 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
    ", 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, "
    " );
    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
    ", ans[i] );
    133 }
    134     
    View Code

    收获:

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

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

     

  • 相关阅读:
    Virtualbox Linux 主机与虚拟机复制粘贴
    解决 VirtualBox里Ubuntu的共享文件夹无法访问的问题
    ayui 单选框、多选框radio 元素判断是必填项 layverify='required'
    layui 复选框checked获取值和赋值
    TypeError: Cannot read properties of undefined (reading 'cancelToken')
    vue2项目部署后 Error: Cannot find module '@/views/*** '
    js 常用的文本过滤转换函数
    Express 接收post
    getActivePinia was called with no active Pinia. Did you forget to install pinia
    Mongoose对象文档无法添加属性
  • 原文地址:https://www.cnblogs.com/idy002/p/4337709.html
Copyright © 2020-2023  润新知