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的子树中。

 /**************************************************************
Problem: 2434
User: idy002
Language: C++
Result: Accepted
Time:580 ms
Memory:18912 kb
****************************************************************/ #include <cstdio>
#include <cstring>
#include <queue>
#define maxn 100010
#define fprintf(...)
using namespace std; struct Query {
int a, id;
Query( int a, int id ):a(a),id(id){}
}; int n, m, ans[maxn];
char str[maxn];
int son[maxn][], pre[maxn], fail[maxn], ppos[maxn], ntot;
int ww[maxn], dl[maxn], dr[maxn], dfs_clock;
vector<int> g[maxn];
vector<Query> qry[maxn]; void modify( int pos, int delta ) {
for( int i=pos; i<=n; i+=i&-i )
ww[i] += delta;
}
int query( int pos ) {
int rt=;
for( int i=pos; i; i-=i&-i )
rt += ww[i];
return rt;
}
void build_trie( int n, const char *P ) {
int u = ;
int pid_clock=;
for( int i=; i<n; i++ ) {
if( P[i]=='P' ) {
pid_clock++;
ppos[pid_clock] = u;
fprintf( stderr, "the No.%d P is at node %d\n", pid_clock, u );
} else if( P[i]=='B' ) {
u = pre[u];
fprintf( stderr, "up to %d\n", u );
} else {
int c=P[i]-'a';
if( !son[u][c] ) {
ntot++;
son[u][c] = ntot;
pre[ntot] = u;
}
fprintf( stderr, "%d->%d with %c\n", u, son[u][c], c+'a' );
u = son[u][c];
}
}
}
void build_fail() {
queue<int> qu;
for( int c=; c<; c++ ) {
int v=son[][c];
if( !v ) continue;
fail[v] = ;
fprintf( stderr, "fail[%d] = %d\n", v, fail[v] );
g[].push_back(v);
qu.push( v );
}
while( !qu.empty() ) {
int u=qu.front();
qu.pop();
for( int c=; c<; c++ ) {
int v=son[u][c];
int w=fail[u];
if( !v ) continue;
while( w && !son[w][c] ) w=fail[w];
fail[v] = son[w][c];
fprintf( stderr, "fail[%d] = %d\n", v, fail[v] );
g[son[w][c]].push_back( v );
qu.push( v );
}
}
}
void dfs( int u ) {
dl[u] = ++dfs_clock;
fprintf( stderr, "(%d ", u );
for( int t=; t<g[u].size(); t++ )
dfs( g[u][t] );
dr[u] = dfs_clock;
fprintf( stderr, ")" );
}
void solve( int n, const char *P ) {
int u=;
for( int i=; i<n; i++ ) {
if( P[i]=='P' ) {
for( int t=; t<qry[u].size(); t++ ) {
Query &q = qry[u][t];
if( q.a==u ) ans[q.id]=;
else ans[q.id] = query(dr[q.a])-query(dl[q.a]-);
}
} else if( P[i]=='B' ) {
if( u== ) continue;
modify( dl[u], - );
u = pre[u];
} else {
int c=P[i]-'a';
u = son[u][c];
modify( dl[u], + );
}
}
}
int main() {
scanf( "%s", str );
n = strlen(str);
build_trie(n,str);
build_fail();
dfs();
fprintf( stderr, "\n" );
scanf( "%d", &m );
for( int i=,a,b; i<=m; i++ ) {
scanf( "%d%d", &a, &b );
a = ppos[a];
b = ppos[b];
qry[b].push_back( Query(a,i) );
}
solve(n,str);
for( int i=; i<=m; i++ )
printf( "%d\n", ans[i] );
}

收获:

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

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

bzoj 2434 ac自动机的更多相关文章

  1. bzoj 2434 AC自动机+树状数组

    2434: [Noi2011]阿狸的打字机 Time Limit: 10 Sec  Memory Limit: 256 MBSubmit: 3493  Solved: 1909[Submit][Sta ...

  2. bzoj 2434 AC自动机 + fail指针建树 + 树状数组

    思路:我们先跟着它给定的字符串走把字典树建出来,求出fail指针,我们考虑两个字符串 A和B, 如果想要求B中有多少A的子串,转换一下就是有多少个B的前缀的后缀包含A,这个在AC自动机 的状态图中很容 ...

  3. bzoj 3172 AC自动机

    初学AC自动机,要先对于每一个模式串求出来trie树,在此基础上构建fail指针,然后在trie树加上失配边构建出整张trie图. AC自动机的原理和KMP差不多,一个节点的fail指针就是指向tri ...

  4. 【无聊放个模板系列】BZOJ 3172 (AC自动机)

    #include<cstdio> #include<cstdlib> #include<cstring> #include<iostream> #inc ...

  5. bzoj 1030 AC自动机+dp

    代码: //先把给的单词建AC自动机并且转移fail,然后d[i][j]表示构造的文章到第i位时处在字典树的第j个节点的不包含单词的数量,最后用总的数量26^m //-d[m][0~sz]即可.其中不 ...

  6. bzoj 2754 ac自动机

    第一道AC自动机题目. 记一下对AC自动机的理解吧: AC自动机=Trie+KMP.即在Trie上应用KMP思想,实现多Pattern的匹配问题. 复杂度是预处理O(segma len(P)),匹配是 ...

  7. bzoj 1030 ac自动机

    比较容易看出来先建立ac自动机,然后在自动机上做DP,设w[0..1][i][j]为当前不包括/包括字典中的字符串,当前在自动机中走到第i个节点,完成的文本的长度为j的方案数,那么比较容易的转移w[i ...

  8. bzoj 1444 AC自动机 + 矩阵乘法 | 高斯消元

    恶补了一下AC自动机,花了一天时间终于全部搞明白了. 思路:将每个人的串加入AC自动机,在AC自动机生成的状态图上建边,注意单词末尾的节点只能转移到自己概率为1, 然后将矩阵自乘几十次后误差就很小了, ...

  9. BZOJ 3940 AC自动机

    思路: 需要维护一个栈的AC自动机--. 要求出来 最后的栈顶是在自动机上的哪个节点. if(!ac.ch[st[tp-1]][a[i]-'a']) st[tp]=ac.ch[ac.f[st[tp-1 ...

随机推荐

  1. Paramiko使用

    1.下载安装 pycrypto-2.6.1.tar.gz (apt-get install python-dev) 解压,进入,python setup.py build[编译],python set ...

  2. favico.js笔记

    1. favicon.js是什么 一个js库可以使用徽标.图像.视频等来设置网页的favicon,即网页标题栏上的小图标. 2. 如何使用 2.1 使用徽标 basic demo: <!DOCT ...

  3. Go语言 5 函数

    文章由作者马志国在博客园的原创,若转载请于明显处标记出处:http://www.cnblogs.com/mazg/ 今天,我们来学习Go语言编程的第五章,函数.首先简单说一下函数的概念和作用.函数是一 ...

  4. three.js为何如此奇妙

    WebGL是在浏览器中实现三维效果的一套规范,而最初使用WebGL原生的API来写3D程序是一件非常痛苦的事情,在辛苦的付出下WebGL开源框架出现了,其中three.js就是非常优秀的一个,它掩盖了 ...

  5. callee与caller

    1.callee arguments.callee表示当前函数,使用于递归 function factorial(num){ if(num<=1){ return 1; }else{ retur ...

  6. (转)USB协议简介

    USB协议简介     USB是一种协议总线,即主机与设备之间的通信需要遵循一系列约定.协议内容较多,这里仅作一些简单介绍,深入学习,可参看USB规范(WWW.usb.org).     为了理解协议 ...

  7. Linux用户密码期限修改

    今天有开发报故,sftp无法登录.检查服务都是正常的,之前3月份也出现过此问题,当时忙没有注意,现在看每3个月出现问题.这才想到是密码过期导致的. 先重置用户密码,发现过期日志为Oct 08, 201 ...

  8. 343.Integer Break---dp

    题目链接:https://leetcode.com/problems/integer-break/description/ 题目大意:给定一个自然数,将其分解,对其分解的数作乘积,找出最大的乘积结果. ...

  9. Xcode及模拟器SDK下载

    http://blog.csdn.net/zhangao0086/article/details/38491271 吐槽下,百度打着无限分享的旗号,却又让分享资源过期,让分享者持续维护 如果你嫌在Ap ...

  10. android 动态改变控件位置和大小 .

    动态改变控件位置的方法: setPadding()的方法更改布局位置. 如我要把Imageview下移200px:             ImageView.setPadding( ImageVie ...