[自動機與樹]

大家都知道的字符串的自動機有--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;
}

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章