您的位置:首页 > 其它

[自动机与树]

2016-03-17 19:49 239 查看
大家都知道的字符串的自动机有--AC自动机啊~后缀自动机啊~

它们很万能~

下面我们讨论一下

[自动机与树]

Part1:AC自动机与fail树

[BZOJ 3172][TJOI 2013]单词
给定一些单词,问每个单词在文章里共出现了多少次

Sample Input

3

a aa aaa

Sample Output

6 3 1

我们有这样一个性质,如果某一个串A是另一个串B的后缀,那么A在fail树上一定是B的祖先

那么就不仅仅要用后缀了

就要在每一个串经过的节点上都++

然后按照逆广搜序update一下就可以啦~

#include
#define maxn 1000010
using namespace std;
int que[maxn];
int n;
char s[maxn];
struct AhoC{
int t[maxn][26], root, size;
int vis[maxn], fail[maxn];
void init(){memset(t, -1, sizeof t);}
int insert(char *c){
int n = strlen(c+1), now = root;
for(int i = 1; i <= n; i ++){
int p = c[i] - 'a';
if(t[now][p] == -1)t[now][p] = ++ size;
now = t[now][p];vis[now] ++;
}return now;
}

void buildfail(){
int head = 0, tail = 0;
for(int i = 0; i < 26; i ++)
if(~t[root][i])que[tail ++] = t[root][i], fail[t[root][i]] = root;
else t[root][i] = root;

while(head != tail){
int u = que[head ++];
for(int i = 0; i < 26; i ++){
if(~t[u][i])que[tail ++] = t[u][i], fail[t[u][i]] = t[fail[u]][i];
else t[u][i] = t[fail[u]][i];
}
}

for(int i = size - 1; ~i; i --)
vis[fail[que[i]]] += vis[que[i]];
}
}AC;
int pos[maxn];
int main(){
AC.init();
scanf("%d", &n);
for(int i = 1; i <= n; i ++){
scanf("%s", s+1);
pos[i] = AC.insert(s);
}
AC.buildfail();
for(int i = 1; i <= n; i ++)
printf("%d\n", AC.vis[pos[i]]);
return 0;
}


BZOJ 2434 NOI2011 阿狸的打字机
啦,有了上一个题目的经验,这个题就是在fail_tree上每次修改一个点的权值,然后查询整棵子树的权值

然后用BIT+DFS序搞一搞就好啦

询问(x, y)的话把它们都挂在y上然后处理完y的时候一块询问就可以啦

#include
#include
#include
#include
#define maxn 200010
using namespace std;

char s[maxn];
int n, root, size;

struct Edge{
int to, next;
}edge[maxn << 3];
int h[maxn], cnt;
void add(int u, int v){
cnt ++;
edge[cnt].to = v;
edge[cnt].next = h[u];
h[u] = cnt;
}

struct Node{int fa, nxt[26];}t[maxn];

void init(){memset(t, -1, sizeof t);}

int pos[maxn], num, Id[maxn];
void buildTire(){
int now = root;
for(int i = 1; i <= n; i ++){
if(s[i] == 'P'){
pos[++ num] = now;
Id[now] = num;
}
else if(s[i] == 'B')
now = t[now].fa;
else{
int p = s[i] - 'a';
if(t[now].nxt[p] == -1)t[now].nxt[p] = ++ size;
t[t[now].nxt[p]].fa = now;
now = t[now].nxt[p];
}
}
}

int que[maxn], fail[maxn];

void buildFail(){
int head = 0, tail = 0;
for(int i = 0; i < 26; i ++)
if(~t[root].nxt[i])que[tail ++] = t[root].nxt[i], fail[t[root].nxt[i]] = root;
else t[root].nxt[i] = root;
while(head != tail){
int now = que[head ++];
for(int i = 0; i < 26; i ++){
if(~t[now].nxt[i])
fail[t[now].nxt[i]] = t[fail[now]].nxt[i],
que[tail ++] = t[now].nxt[i];
else t[now].nxt[i] = t[fail[now]].nxt[i];
}
}
for(int i = 1; i <= size; i ++)
add(fail[i], i);
}

int dfs_clock, To[maxn], Out[maxn], siz[maxn];

void dfs(int u){
To[u] = ++ dfs_clock;
for(int i = h[u]; i; i = edge[i].next)
dfs(edge[i].to);
Out[u] = dfs_clock;
}

int BIT[maxn];
#define lowbit(i) i & ((~i)+1)
void update(int u, int val){
int pos = To[u];
for(int i = pos; i <= dfs_clock+1; i += lowbit(i))
BIT[i] += val;
//pos = Out[u]+1;
//for(int i = pos; i <= dfs_clock+1; i += lowbit(i))BIT[i] -= val;
}

int ask(int u){
int ret = 0;
for(int i = Out[u]; i; i -= lowbit(i))
ret += BIT[i];
for(int i = To[u]-1; i; i -= lowbit(i))
ret -= BIT[i];
return ret;
}

int ans[maxn];

void Query(int u){
int y;
for(int i = h[Id[u]]; i; i = edge[i].next){
y = edge[i].to;
ans[i] = ask(pos[y]);
}
}
int main(){
scanf("%s", s+1);
n = strlen(s+1);
init();
root = ++ size;
buildTire();
buildFail();
dfs(root);
memset(h, 0, sizeof h);
cnt = 0;
int m;
scanf("%d", &m);
int x, y;
for(int i = 1; i <= m; i ++){
scanf("%d%d", &x, &y);
add(y, x);
}
int now = root;
for(int i = 1; i <= n; i ++){
if(s[i] == 'P')Query(now);
else if(s[i] == 'B'){
update(now, -1);
now = t[now].fa;
}
else{
int p = s[i] - 'a';
now = t[now].nxt[p];
update(now, 1);
}
}
for(int i = 1; i <= m; i ++)
printf("%d\n", ans[i]);
return 0;
}


Part2:后缀自动机与parent树

[BZOJ 2780][SPOJ 8093] Sevenk Love Oimaster

题意:给定义一个字符串集合,再给定一些字符串,对于每个串询问它在几个串里出现过。
3 3

abcabcabc     --------字符串集合

aaa

aafe

abc                --------询问字符串

a

ca

Sample Output

1 3 1

广义后缀自动机:
对于多个串建立一个自动机
合并路径
给每个节点打上一个属于第几个串的标记
对于parent树上的一个节点
它要求的是它的子树中有多少种不同的标记
离线询问

对于每个值保留当前询问的第一个出现的标记
具体请自行搜索HH的项链--

#include
#define maxn 500000
using namespace std;

int n, m;

char s[maxn];

struct Edge_{int to, next;};
int In[maxn], Out[maxn], dfs_clock, dfn[maxn], ans[maxn];
vectornxt[maxn];
int vis[maxn];
namespace BIT{
int t[maxn];
#define lowbit(i) i&(~i+1)
void update(int pos, int val){
if(!pos)return;
for(int i = pos; i <= dfs_clock; i += lowbit(i))
t[i] += val;
}

int ask(int pos){
if(!pos)return 0;
int ret = 0;
for(int i = pos; i; i -= lowbit(i))
ret += t[i];
return ret;
}
}

struct Edge{
Edge_ edge[maxn];
int h[maxn], cnt;
void add(int u, int v){
cnt ++;
edge[cnt].to = v;
edge[cnt].next = h[u];
h[u] = cnt;
}

void dfs(int u){
In[u] = ++ dfs_clock;
dfn[dfs_clock] = u;
for(int i = h[u]; i; i = edge[i].next)
dfs(edge[i].to);
Out[u] = dfs_clock;
}

void solve(){
for(int i = dfs_clock; i; i --){
int now = dfn[i];
for(int j = h[now]; j; j = edge[j].next){
int v = edge[j].to;
if(vis[v])nxt[i].push_back(vis[v]);
vis[v] = i;
}
}
for(int i = 1; i <= n; i ++)
BIT::update(vis[i], 1);
}
}A, B;

struct Node{
int len, link;
mapnxt;
}st[maxn];

int root, size, last;

void init(){
root = size = last = 0;
st[root].len = 0;
st[root].link = -1;
}

void Extend(char ch, int Id){
int c = ch - 'a', p = last, q = st[p].nxt[c];
if(q){
if(st[q].len == st[p].len + 1)
last = q;
else{
int clone = ++ size;
st[clone] = st[q];
st[clone].len = st[p].len + 1;
for(; ~p && st[p].nxt[c] == q; p = st[p].link)
st[p].nxt[c] = clone;
st[q].link = clone;
last = clone;
}
}
else{
int cur = ++ size;
st[cur].len = st[p].len + 1;
for(; ~p && !st[p].nxt[c]; p = st[p].link)
st[p].nxt[c] = cur;
if(p == -1)
st[cur].link = root;
else{
q = st[p].nxt[c];
if(st[q].len == st[p].len + 1)
st[cur].link = q;
else{
int clone = ++ size;
st[clone] = st[q];
st[clone].len = st[p].len + 1;
for(; ~p && st[p].nxt[c] == q; p = st[p].link)
st[p].nxt[c] = clone;
st[q].link = st[cur].link = clone;
}
}
last = cur;
}
A.add(last, Id);
}

struct opt{
int l, r, id;
bool operator<(const opt& k)const{
if(l != k.l)return l < k.l;
return r < k.r;
}
}q[maxn];

int main(){
init();
scanf("%d%d", &n, &m);
for(int i = 1; i <= n; i ++){
scanf("%s", s+1);
int N = strlen(s+1);
last = root;
for(int j = 1; j <= N; j ++)
Extend(s[j], i);
}

for(int i = 1; i <= size; i ++)
B.add(st[i].link, i);
B.dfs(root);
A.solve();

int tot = 0;
for(int i = 1; i <= m; i ++){
scanf("%s", s+1);
int N = strlen(s+1), now = root;
bool flag = true;
for(int j = 1; j <= N; j ++){
int p = s[j] - 'a';
if(!st[now].nxt[p]){
flag = false;
break;
}
now = st[now].nxt[p];
}

if(flag){
++ tot;
q[tot].l = In[now];
q[tot].r = Out[now];
q[tot].id = i;
}
}

sort(q+1, q+1+tot);
int l = 1;
for(int i = 1; i <= tot; i ++){
while(l < q[i].l && l < dfs_clock){
for(int j = 0; j < nxt[l].size(); j ++)
BIT::update(nxt[l][j], 1);
l ++;
}
ans[q[i].id] = BIT::ask(q[i].r) - BIT::ask(q[i].l-1);
}
for(int i = 1; i <= m; i ++)
printf("%d\n", ans[i]);
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  SAM AC自动机