现在的位置: 首页 > 综合 > 正文

AC自动机 fail树 线段树维护

2012年11月03日 ⁄ 综合 ⁄ 共 5308字 ⁄ 字号 评论关闭

http://codeforces.com/problemset/problem/163/E

http://acm.hdu.edu.cn/showproblem.php?pid=4117

上面两题我都是用AC自动机 + 线段树写的

当我们用AC自动机解决DP 或者 统计问题的时候,如果要支持更新操作,就需要数据结构的帮忙了

比如codeforces 163E,背景是最简单的多串匹配,但是有一个特殊的地方是会删除一些字符串和重新恢复一些字符串,注意到我们在统计的时候其实就是沿着fail指针走,把所有的标记叠加起来,而fail指针构成了一棵fail树,所以我们在求当前节点的fail指针方向有多少个标记的时候不必一层层的fail上去了,对于每个点维护其到根的有效节点的个数即可,

当更新某个点的时候,就相当于这个点的子树到根的有效节点的个数都发生了变化,将树形结构变成线性结构,在线段树中更新即可


第二题是个DP题,也是类似的方法,在fail指针方向找一个最大的值进行DP的转移,利用线段树求极值


codeforces 163E

#include<cstdio>
#include<cstring>
#include<set>
#include<string>
#include<iostream>
#include<cmath>
#include<vector>
#include<map>
#include<stack>
#include<time.h>
#include<queue>
#include<cstdlib>
#include<algorithm>
using namespace std;
#define lowbit(x) ((x)&(-(x)))
#define sqr(x) ((x)*(x))
#define PB push_back
#define MP make_pair
typedef long long lld;
typedef vector<int> VI;
typedef vector<string> VS;
typedef pair<int,int> PII;
#define lson l,m,rt<<1
#define rson m+1,r,rt<<1|1
const int inf = ~0u>>2;
const int maxn = 1000010;
// ~ segment tree
int add[maxn<<2];
int sum[maxn<<2];
int L[maxn],R[maxn];
//AC auto
const int M = maxn;
const int CD = 26;
int sz;
int ID[128];
int val[M];
int fail[M];
int Q[M];
int ch[M][CD];
int mp[M];
//failtree
vector<int> edge[maxn];
int tot;
void Init()
{
	fail[0]=0;  val[0]=0;
	sz=1;
	memset(ch[0],0,sizeof(ch[0]));
	for(int i=0;i<26;i++) ID[i+'a']=i;
}
void Insert(char *s,int id){
	int p=0;
	for( ; *s ; s++)	{
		int c=ID[*s];
		if(!ch[p][c])  {
			memset(ch[sz],0,sizeof(ch[sz]));
			val[sz] = 0;
			ch[p][c] = sz++;
		}
		p=ch[p][c];
	}
	val[p] = 1;
	mp[id] = p;
}
void Construct() {
	int *s=Q,*e=Q;
	for(int i=0;i<CD;i++) {
		if(ch[0][i]) {
			fail[ ch[0][i] ] = 0;
			*e++ = ch[0][i];
		}
	}
	while(s!=e) {
		int u = *s++;
		for(int i=0;i<CD;i++) {
			int &v = ch[u][i];
			if(v) {
				*e++  = v;
				fail[v] = ch[fail[u]][i];
			} else {
				v=ch[fail[u]][i];
			}
		}
	}
}
void dfs(int u)  {
	L[u]=++tot;
	for(int i=0;i<edge[u].size();i++){
		int v=edge[u][i];
		dfs(v);
	}
	R[u]=tot;
}
void build(int l,int r,int rt) {
	add[rt] = sum[rt] =0;
	if(l == r)   {
		return ;
	}
	int m = l+r>>1;
	build(lson);
	build(rson);
}
void pushdown(int rt,int m)
{
	if(add[rt])
	{
		add[rt<<1] += add[rt];
		add[rt<<1|1] += add[rt];
		sum[rt<<1] += add[rt] * (m-(m>>1));
		sum[rt<<1|1] += add[rt]*(m>>1);
		add[rt]=0;
	}
}
void pushup(int rt)
{
	sum[rt] = sum[rt<<1] + sum[rt<<1|1];
}
void update(int L,int R,int v,int l,int r,int rt)
{
	if(L <= l && r <= R)
	{
		add[rt] += v;
		sum[rt] += v;
		return ;
	}
	pushdown(rt,r-l+1);
	int m=l+r>>1;
    if(L <= m) update(L , R , v , lson);
	if(R > m)  update(L , R , v , rson);
	pushup(rt);
}
int query(int p,int l,int r,int rt)
{
	if(l == r)
	{
		return  sum[rt];
	}
	pushdown(rt,r-l+1);
	int m = l+r>>1;
	if(p <= m) return query(p,lson);
	if(p > m) return query(p,rson);
}
void failtree_init()
{
	tot=0;
	for(int i=1;i<sz;i++)	edge[fail[i]].push_back(i);
	dfs(0);
	build(1,sz,1);
	for(int i=1;i<sz;i++)
	{
		if(val[i]) 
		{
			update(L[i],R[i],1,1,sz,1);
		}
	}
}
void AC(char *s)
{
	int p=0,ans=0;
	for( ; *s ; s++)
	{
		int c = ID[*s];
		p = ch[p][c];
		ans+=query(L[p],1,sz,1);
	}
	printf("%d\n",ans);
}
int transform(char *s)
{
	int num=0;
	for(;*s;s++)
	{
		num=num*10+*s-'0';
	}
	return num;
}
char s[maxn];
bool flag[100010];
int main()
{
	int n,k;
	scanf("%d%d",&n,&k);
	Init();
	for(int i=0;i<k;i++)
	{
		scanf("%s",s);
		Insert(s,i+1);
	}
	memset(flag,true,sizeof(flag));
	Construct();
	failtree_init();
	while(n--)
	{
		scanf("%s",s);
		if(s[0]=='?')
		{
			AC(s+1);
		}
		else if(s[0]=='+')
		{
			int num=transform(s+1);
			if(flag[num]) continue;
			flag[num]=true;
			update(L[mp[num]],R[mp[num]],1,1,sz,1);
		}
		else 
		{
			int num=transform(s+1);
			if(!flag[num]) continue;
			flag[num]=false;
			update(L[mp[num]],R[mp[num]],-1,1,sz,1);
		}
	}
	return  0;
}
/*
100 3
bbaa
baa
aa
-2
?baabaa
*/

hdu 4117

#include<cstdio>
#include<cstring>
#include<vector>
#include<algorithm>
using namespace std;
#define lson l,m,rt<<1
#define rson m+1,r,rt<<1|1
inline void Max(int &a,int b){if(b>a) a=b;}
const int M  =  300010;
const int N  =  20010;
const int CD = 26;
int fail[M];
int Q[M];
int ch[M][CD];
int ID[128];
int sz;
char S[M];
int node[M];
void Init(){
    fail[0]=0;
    memset(ch[0],0,sizeof(ch[0]));
    sz=1;
    for(int i=0;i<26;i++) ID[i+'a'] = i;
}
void Insert(char *s,int id) {
     int p=0;
     for(;*s;s++) {
         int c=ID[*s];
         if(!ch[p][c]) {
             memset(ch[sz],0,sizeof(ch[sz]));
             ch[p][c]=sz++;
         }
         p=ch[p][c];
     }
     node[id]=p;
}
void Construct(){
    int *s=Q,*e=Q,v;
    for(int i=0;i<CD;i++) {
        if(ch[0][i]) {
            fail[ch[0][i]] = 0;
            *e++ = ch[0][i];
        }
    }
    while(s!=e) {
        int u= *s++;
        for(int i=0;i<CD;i++) {
            if(v = ch[u][i]) {
                *e++ = v;
                fail[v]  = ch[fail[u]][i];
            }  else {
                ch[u][i] =ch[fail[u]][i];
            }
        }
    }
}
vector<int> edge[M];
int n;
int pos[N];
int tot;
int mx[M<<2];
int lz[M<<2];
void pushup(int rt) {
    mx[rt] = max(mx[rt<<1],mx[rt<<1|1]);
}
void pushdown(int rt) {
     if(lz[rt]) {
        Max(lz[rt<<1],lz[rt]);
        Max(lz[rt<<1|1],lz[rt]);
        Max(mx[rt<<1],lz[rt<<1]);
        Max(mx[rt<<1|1],lz[rt<<1|1]);
        lz[rt]=0;
     }
}
void build(int l,int r,int rt) {
    lz[rt] = mx[rt] = 0;
    if(l==r) return ;
    int m=l+r>>1;
    build(lson);
    build(rson);
}
void update(int L,int R,int val,int l,int r,int rt) {
    if(L <= l && r <= R) {
        Max(lz[rt],val);
        Max(mx[rt],val);
        return ;
    }
    pushdown(rt);
    int m=l+r>>1;
    if(L <= m) update(L,R,val,lson);
    if(R > m) update(L,R,val,rson);
    pushup(rt);
}
int query(int pos,int l,int r,int rt) {
    if(l==r) {
        return mx[rt];
    }
    pushdown(rt);
    int m=l+r>>1;
    if(pos <= m) return query(pos,lson);
    return query(pos,rson);
}
int L[M],R[M],dp[M];
void dfs(int u){
    L[u]=++tot;
    for(int i=0;i<edge[u].size();i++) {
        int v=edge[u][i];
        dfs(v);
    }
    R[u]=tot;
}
void AC(int id,int l,int r){
    int now=0,v=0;
    for(int i=l;i<=r;i++){
        int c=ID[S[i]];
        now=ch[now][c];
        Max(v,query(L[now],1,tot,1));
    }
    dp[id]+=v;
    update(L[node[id]],R[node[id]],dp[id],1,tot,1);
}
int main(){
    int t,ca=1;
    scanf("%d",&t);
    while(t--){
        scanf("%d",&n);
        Init();
        for(int i=1;i<=n;i++)  {
            scanf("%s%d",S+pos[i-1]+1,&dp[i]);
            Insert(S+pos[i-1]+1,i);
            pos[i] = strlen(S+1+pos[i-1]) + pos[i-1];
        }
        Construct();
        for(int i=0;i<=sz;i++) edge[i].clear();
        for(int i=1;i<=sz;i++) edge[fail[i]].push_back(i);
        tot=0;
        dfs(0);
        build(1,tot,1);
        int ans=0;
        for(int i=1;i<=n;i++) {
            if(dp[i]>0) AC(i,pos[i-1]+1,pos[i]);
            Max(ans,dp[i]);
        }
        printf("Case #%d: %d\n",ca++,ans);
    }
    return 0;
}

抱歉!评论已关闭.