题目:给出n个串,问最多能够选出多少个串,使得前面串是后面串的子串(按照输入顺序)
分析:
其实这题是这题SPOJ 7758. Growing Strings AC自动机DP的进阶版本,主题思想差不多。
对于这题来说,需要离线操作。dp转移也是很显然。
但是由于数据比较大,所以普通的沿着fail指针往上走,逐步更新答案会TLE。
考虑把fail指针反向,由于ac自动机的每个节点均有唯一的fail指针,若是沿着fail指针往上走,显然都会走到root,所以反向之后显然是一棵树,不妨称之为fail树。fail树有什么特点呢?可以画个图试试,如果儿子节点出现过,那么他的祖先显然也会出现!
因此,正确的做法时:
建立所有单词的AC自动机,对于每个节点的转移,都是从parent[]或者从fail[],fail[fail[]],...得到的。可以看出fail[]的关系形成一棵树,于是问题转化成,不断在节点处插入,询问点到根路径上的最大值,可以利用dfs序列转化用线段树维护。
#include
#include
#include
#include
#include
#include
#include
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
#define debug puts("here")
#define rep(i,n) for(int i=0;i
#define PQ priority_queue
#define cmax(x,y) x = max(x,y)
#define cmin(x,y) x = min(x,y)
#define Clear(x) memset(x,0,sizeof(x))
#define lson rt<<1
#define rson rt<<1|1
#define SZ(x) x.size()
/******** program ********************/
const int MAXN = 3e5+5;
const int kind = 26;
char s[MAXN];
int beg[MAXN];
struct segTree {
int l,r,mx;
int cov;
inline int mid() {
return (l+r)>>1;
}
} tree[MAXN<<2];
void build(int l,int r,int rt) {
tree[rt].l = l;
tree[rt].r = r;
tree[rt].cov = tree[rt].mx = 0;
if(l==r)return;
int mid = tree[rt].mid();
build(l,mid,lson);
build(mid+1,r,rson);
}
void update(int rt){
if(tree[rt].cov==0)return;
cmax( tree[lson].cov,tree[rt].cov );
cmax( tree[rson].cov,tree[rt].cov );
cmax( tree[lson].mx,tree[rt].cov );
cmax( tree[rson].mx,tree[rt].cov );
tree[rt].cov = 0;
}
void modify(int l,int r,int mx,int rt) {
if(l<=tree[rt].l&&tree[rt].r<=r) {
cmax(tree[rt].mx,mx);
cmax(tree[rt].cov,mx);
return;
}
update(rt);
int mid = tree[rt].mid();
if(r<=mid)modify(l,r,mx,lson);
else if(l>mid)modify(l,r,mx,rson);
else {
modify(l,r,mx,lson);
modify(l,r,mx,rson);
}
tree[rt].mx = max( tree[lson].mx,tree[rson].mx );
}
int ask(int pos,int rt) {
if(tree[rt].l==tree[rt].r)
return tree[rt].mx;
update(rt);
int mid = tree[rt].mid();
if(pos<=mid)return ask(pos,lson);
else return ask(pos,rson);
}
int ch[MAXN][kind],fail[MAXN];
int val[MAXN];
int st[MAXN],ed[MAXN];
int tot,tim;
vector adj[MAXN];
inline void set(int x) {
Clear(ch[x]);
fail[x] = 0;
adj[x].clear();
}
inline void init() {
set(1);
tot = 1;
}
inline int newNode() {
set(++tot);
return tot;
}
inline int ind(char c) {
return c-'a';
}
inline void ins(int x,int y,int val) {
int r = 1;
REP(i,x,y) {
int c = ind(s[i]);
if(ch[r][c]==0)
ch[r][c] = newNode();
r = ch[r][c];
}
}
inline void build() {
queue q;
q.push(1);
while(!q.empty()) {
int r = q.front();
q.pop();
if(fail[r])
adj[ fail[r] ].pb(r);
rep(c,kind) {
int x = ch[r][c];
if(!x)continue;
q.push(x);
int y = fail[r];
while(y&&ch[y][c]==0)
y = fail[y];
fail[x] = y?ch[y][c]:1;
}
}
}
void dfs(int x) {
st[x] = ++ tim;
foreach(i,adj[x]) {
int y = adj[x][i];
dfs(y);
}
ed[x] = tim;
}
inline int run() {
init();
int n;
RD(n);
rep1(i,n) {
scanf("%s",s+beg[i-1]);
RD(val[i]);
beg[i] = beg[i-1]+strlen(s+beg[i-1]);
ins(beg[i-1],beg[i]-1,val[i]);
}
build();
tim = 0;
dfs(1);
build(1,tim,1);
int ans = 0;
rep1(i,n) {
int r = 1;
int now = 0;
for(int j=beg[i-1]; j