[自动机与树]

大家都知道的字符串的自动机有--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];
vector
    
    
     
     nxt[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;
	map
     
     
      
      nxt;
}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;
}

     
     
    
    
   
   


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值