LOJ#6198. 谢特 SAM+启发式合并+01trie
并不难的一道字符串题.
显然后缀自动机上进行字典树的启发式合并.
但是一定注意,题中要求的是两个后缀的 LCP 而不是两个前缀的 LCP.
所以在构建后缀自动机的时候要从后向前构建.
刚开始从前向后构建 WA 了半天.
然后进行启发式合并的时候可以对每个节点维护一个 id[x],如果儿子的大小大于点 $x$ 大小就 swap 一下即可.
code:
#include <cstdio>
#include <vector>
#include <cstring>
#include <algorithm>
#define N 200008
#define ll long long
#define pb push_back
#define setIO(s) freopen(s".in","r",stdin)
using namespace std;
int ans;
int last,tot,cnt,n,edges;
char str[N];
vector<int>se[N];
int hd[N],to[N],nex[N];
int pre[N],mx[N],ch[N][27],id[N],rt[N],val[N];
struct data {
int ch[2],si;
}s[N*50];
void add(int u,int v) {
nex[++edges]=hd[u];
hd[u]=edges,to[edges]=v;
}
void ins(int &x,int l,int v) {
if(!x) {
x=++cnt;
}
if(l==-1) {
return;
}
int d=(v>>l)&1;
ins(s[x].ch[d],l-1,v),++s[s[x].ch[d]].si;
}
int query(int x,int l,int v) {
if(l==-1) {
return 0;
}
int d=1^((v>>l)&1);
if(s[x].ch[d]) {
return (1<<l)+query(s[x].ch[d],l-1,v);
}
else {
return query(s[x].ch[1^d],l-1,v);
}
}
void init() {
last=tot=1;
for(int i=1;i<N;++i) id[i]=i;
}
void extend(int c,int v) {
int np=++tot,p=last;
mx[np]=mx[p]+1,last=np;
for(;p&&!ch[p][c];p=pre[p]) ch[p][c]=np;
if(!p) {
pre[np]=1;
}
else {
int q=ch[p][c];
if(mx[q]==mx[p]+1) pre[np]=q;
else {
int nq=++tot;
mx[nq]=mx[p]+1;
memcpy(ch[nq],ch[q],sizeof(ch[q]));
pre[nq]=pre[q],pre[q]=pre[np]=nq;
for(;p&&ch[p][c]==q;p=pre[p]) ch[p][c]=nq;
}
}
se[np].pb(v);
ins(rt[np],22,val[v]);
}
void sol(int x) {
int y,z;
for(int i=hd[x];i;i=nex[i]) {
y=to[i],sol(y);
if(se[id[x]].size()<se[id[y]].size()) {
swap(id[x],id[y]);
}
for(int j=0;j<se[id[y]].size();++j) {
int p=se[id[y]][j];
int tmp=mx[x]+query(rt[id[x]],22,val[p]);
ins(rt[id[x]],22,val[p]);
se[id[x]].pb(p);
if(tmp>ans) {
ans=tmp;
}
}
}
}
int main() {
// setIO("input");
init();
scanf("%d%s",&n,str+1);
for(int i=1;i<=n;++i) {
scanf("%d",&val[i]);
}
for(int i=1;i<=n;++i) {
extend(str[n-i+1]-'a',n-i+1);
}
for(int i=2;i<=tot;++i) {
add(pre[i],i);
}
sol(1);
printf("%d\n",ans);
return 0;
}

浙公网安备 33010602011771号