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. long类型的数据转化为时间

    long time = 111111111111111111111:SimpleDateFormat sdf= new SimpleDateFormat("yyyy-MM-dd HH:mm: ...

  2. webpack中的静态资源处理

    你可能已经注意到,在我们的项目结构里,有两个静态文件的路径,分别是:src/assets 和 static/.那这两个到底有什么区别呢? Webpacked 资源 为了回答这个问题,我们首先需要理解w ...

  3. Python异常捕捉try except else finally有return时执行顺序探究

    转载自 https://www.cnblogs.com/JohnABC/p/4065437.html 学习python或者其他有异常控制的编程语 言, 大家很有可能说try except finall ...

  4. 【swupdate文档 四】SWUpdate:使用默认解析器的语法和标记

    SWUpdate:使用默认解析器的语法和标记 介绍 SWUpdate使用库"libconfig"作为镜像描述的默认解析器. 但是,可以扩展SWUpdate并添加一个自己的解析器, ...

  5. Asia-Dhaka 2017

    Asia-Dhaka 2017 A - Brick Walls 题目描述:如下图,编坐标与路径,给出两个坐标,问两个坐标的最短距离是多少. solution 先阶梯型地走,然后注意"中&qu ...

  6. React.js学习之理解JSX和组件

    在开启JSX的学习旅程前,我们先了解一下React的基本原理.React本质上是一个"状态机",它只关心两件事:更新DOM和响应事件,React不处理Ajax.路由和数据存储,也不 ...

  7. js监测设备类型【安卓,ios,苹果微信,电脑pc】

    话不多说上代码: 1.判断是不是微信 function is_weixn(){ var ua = navigator.userAgent.toLowerCase(); if(ua.match(/Mic ...

  8. Window 下一台机器配置三个Tomcat实例

    下面我们把配置的详细过程写在下面,以供参考:(此例以配置三个Tomcat为例) 1. 下载apache-tomcat-8.0.63,下载下来的文件为apache-tomcat-8.0.63.zip. ...

  9. 解决CentOS7关闭/开启防火墙出现Unit iptables.service failed to load: No such file or directory.

    CentOS7中执行 service iptables start/stop 会报错Failed to start iptables.service: Unit iptables.service fa ...

  10. 【ASP.NET】编写自己的Web服务器

    自己写一个简单的Web服务器,对加深理解Http协议有很好的帮助,下面就看一下一个基于TcpListener的Web服务器: class Program { static void Main(stri ...