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;
}