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 ...
随机推荐
- npm的常用命令
npm install <name>安装nodejs的依赖包 例如npm install express 就会默认安装express的最新版本,也可以通过在后面加版本号的方式安装指定版本, ...
- 【Tomcat】tomcat中server.xml配置详解
Tomcat Server的结构图如下: 该文件描述了如何启动Tomcat Server <Server> <Listener /> <GlobaNaming ...
- javascript你不知道的This
<你不知道的javascript>这本书读了有好几遍了,似乎每一次读都有新发现,有些内容并不是一下子可以弄懂的,每次读似乎都能明白一些概念.再重读一下this关键字.这个概念非常灵活,也非 ...
- input只读属性 设置和移除 选择数字
设置只读属性 $('#stage').attr("readonly", "readonly"); 移除 只读属性 $("input").r ...
- PHP--- JSON和数组的转换
一.json_encode() <?php $arr =array ('a'=>1,'b'=>2,'c'=>3,'d'=>4,'e'=>5); echo json_ ...
- rds 与mysql 进行主从同步
.rds上默认会有server-****,只需要配置从数据库: .从数据库的配置流程: .[mysqld] log-bin = mysql-bin-changelog #要和主库中的名字一样 rela ...
- 最全Pycharm教程(26)——Pycharm搜索导航之文件名、符号名搜索(转)
1.准备一个工程 向你的工程中添加一个Python文件,并输入一些源码,例如: 2.转到对应文件.类.符号 Pycharm提供的一个很强力的功能就是能够根据名称跳转到任何文件.类.符号所在定义位置. ...
- HTML+CSS小技巧
网页标题前引入ico图标 <link rel="shortcut icon" href="img/icoTest.ico">
- 关于Windows中的硬链接
https://zhidao.baidu.com/question/748233720330351012.html linux中使用硬链接 ln a.txt b.txt 查看硬链接 ls -il 关于 ...
- MVC – 9.mvc整体请求流程
1.请求管道 2~5微软自己的验证,我们一般不用. 在全局配置文件中-已经配置一个路由过滤器-为第7个事件注册了路由方法 1.在application_start中向静态路由表注册了路由数据,在管道第 ...