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 */
#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; }