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的子树中。
/**************************************************************
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自动机的更多相关文章
- bzoj 2434 AC自动机+树状数组
2434: [Noi2011]阿狸的打字机 Time Limit: 10 Sec Memory Limit: 256 MBSubmit: 3493 Solved: 1909[Submit][Sta ...
- bzoj 2434 AC自动机 + fail指针建树 + 树状数组
思路:我们先跟着它给定的字符串走把字典树建出来,求出fail指针,我们考虑两个字符串 A和B, 如果想要求B中有多少A的子串,转换一下就是有多少个B的前缀的后缀包含A,这个在AC自动机 的状态图中很容 ...
- bzoj 3172 AC自动机
初学AC自动机,要先对于每一个模式串求出来trie树,在此基础上构建fail指针,然后在trie树加上失配边构建出整张trie图. AC自动机的原理和KMP差不多,一个节点的fail指针就是指向tri ...
- 【无聊放个模板系列】BZOJ 3172 (AC自动机)
#include<cstdio> #include<cstdlib> #include<cstring> #include<iostream> #inc ...
- bzoj 1030 AC自动机+dp
代码: //先把给的单词建AC自动机并且转移fail,然后d[i][j]表示构造的文章到第i位时处在字典树的第j个节点的不包含单词的数量,最后用总的数量26^m //-d[m][0~sz]即可.其中不 ...
- bzoj 2754 ac自动机
第一道AC自动机题目. 记一下对AC自动机的理解吧: AC自动机=Trie+KMP.即在Trie上应用KMP思想,实现多Pattern的匹配问题. 复杂度是预处理O(segma len(P)),匹配是 ...
- bzoj 1030 ac自动机
比较容易看出来先建立ac自动机,然后在自动机上做DP,设w[0..1][i][j]为当前不包括/包括字典中的字符串,当前在自动机中走到第i个节点,完成的文本的长度为j的方案数,那么比较容易的转移w[i ...
- bzoj 1444 AC自动机 + 矩阵乘法 | 高斯消元
恶补了一下AC自动机,花了一天时间终于全部搞明白了. 思路:将每个人的串加入AC自动机,在AC自动机生成的状态图上建边,注意单词末尾的节点只能转移到自己概率为1, 然后将矩阵自乘几十次后误差就很小了, ...
- BZOJ 3940 AC自动机
思路: 需要维护一个栈的AC自动机--. 要求出来 最后的栈顶是在自动机上的哪个节点. if(!ac.ch[st[tp-1]][a[i]-'a']) st[tp]=ac.ch[ac.f[st[tp-1 ...
随机推荐
- webconfig的配置解析
<?xml version="1.0"?> <!--注意: 除了手动编辑此文件以外,您还可以使用 Web 管理工具来配置应用程序的设置.可以使用 Visual S ...
- python并发编程之asyncio协程(三)
协程实现了在单线程下的并发,每个协程共享线程的几乎所有的资源,除了协程自己私有的上下文栈:协程的切换属于程序级别的切换,对于操作系统来说是无感知的,因此切换速度更快.开销更小.效率更高,在有多IO操作 ...
- dev_alloc_skb(len+16) skb_reserve(skb,2) skb_put(skb,len)
/** * dev_alloc_skb - allocate an skbuff for receiving * @length: length to allocate * * ...
- mac下 mysql / nginx 问题总汇
"ERROR 2002 (HY000): Can't connect to local MySQL server through socket '/tmp/mysql.sock' (2)&q ...
- C/C++——库函数strcpy和strdup比较
版权声明:原创文章,禁止转载. 1. strcpy 原型: extern char *strcpy(char *dest,char *src); 用法: #include <string.h&g ...
- Linux 基础目录和命令
Linux 标准目录结构 初学Linux,首先需要弄清Linux 标准目录结构 / root --- 启动Linux时使用的一些核心文件.如操作系统内核.引导程序Grub等. home --- 存 ...
- Django 中form的用法
form的主要作用:1.在html中生成表单框架,2.验证数据(实话实说,很简洁,但不实用,灵活性差) from django.db import models # Create your model ...
- centos6下mysql的主从复制的配置
2015年9月17日 23:00:36 update 想要好好了解mysql复制,还是去看看<高性能MySQL>(第三版)好了,上面说的比较详细. =========== 在本地用virt ...
- Mybatis的关联映射
实际的开发中,对数据库的操作常常会涉及到多张表,这在面向对象中就涉及到了对象与对象之间的关联关系.针对多表之间的操作,MyBatis提供了关联映射, 通过关联映射就可以很好的处理对象与对象之间的关联关 ...
- **CodeIgniter通过hook的方式实现简单的权限控制
根据自己的实际情况,需要两个文件,一个是权限控制类,Acl,另外一个是权限配置的文件acl.php放在了config这个目录下. Acl这个类放在了application/hook/acl.php.通 ...