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 ...
随机推荐
- HDU 1577 WisKey的眼神 (找规律 数学)
题目链接 Problem DescriptionWisKey的眼镜有500多度,所以眼神不大好,而且他有个习惯,就是走路喜欢看着地(不是为了拣钱哦^_^),所以大家下次碰见他的时候最好主动打下招呼,呵 ...
- linux initcall 介绍 (转自http://blog.csdn.net/fenzhikeji/article/details/6860143)
现在以module_init为例分析initcall在内核中的调用顺序 在头文件init.h中,有如下定义: #define module_init(x) __initcall(x); 很明显 ...
- FPGA与CPLD的概念及其区别
一.FPGA与CPLD的基本概念 1.CPLD CPLD主要是由可编程逻辑宏单元(LMC,Logic Macro Cell)围绕中心的可编程互连矩阵单元组成,其中LMC逻辑结构较复杂,并具有复杂的I/ ...
- 144.Binary Tree Preorder Traversal---二叉树先序、中序非递归遍历
题目链接 题目大意:返回二叉树的先序遍历list.中序见94,后序见145. 法一:普通递归遍历,只是这里多了一个list数组,所以分成了两个函数.代码如下(耗时1ms): public List&l ...
- Ubuntu或者Ubuntu server重新设置IP地址
1.打开终端输入: sudo vi /etc/network/interfaces 2.进入编辑页面 改一处,添加5行内容,如下图: 3.修改好后esc :wq进行保存 4.输入: sudo / ...
- MySQL 操作总结
1. 数据库级别操作 1.1 创建数据库 CREATE DATABASE db1 default charset utf8 collate utf8_general_ci; 1.2 删除数据库 DRO ...
- 使用非root用户启动tomcat
以下操作均为以root用户运行1.添加tomcat用户组 /usr/sbin/groupadd tomcat 2.添加tomcat用户,并限制登录 /usr/sbin/useradd -s /bin/ ...
- 转:PHP环境搭建 - Linux
本文PHP环境采用,nginx + PHP7 + mysql 5.6 一.安装mysql 5.6 参见:http://www.cnblogs.com/rslai/p/7853465.html 二.Ng ...
- ntp 校时程序
//effect:send ntp packet and get the ntp packet ,make the time OK//2014.7.31 is OK//#include <sys ...
- Django-manage.py
一.manage.py命令选项 manage.py是每个Django项目中自动生成的一个用于管理项目的脚本文件,需要通过python命令执行.manage.py接受的是Django提供的内置命令. 内 ...