标签:区间 %s href lan query 集合 false com ace
这题有SA和SAM两种做法,但事实证明,本题的SAM做法无论在思维难度还是在代码难度上,都爆踩SA做法。
首先,SA做法可以参见本人的题解。
然后,SAM做法见下。
首先,我们一定可以将每个串砍掉一部分,使得我们所需串中,前一个串必是后一个串的后缀。具体而言,因为前一个串必定在后一个串中出现至少两次,于是我们就砍掉后一个串的后缀,使得前一个串的最后一次出现的结尾处即为后一个串的结尾。这样,被砍掉后缀的后一个串,仍然至少在后后一个串中出现两次,则此方案合法。
于是我们考虑在SAM上从上往下DP。设 \(f_i\) 表示节点 \(i\) 所对应的 \(\text{endpos}\) 集合中最长串所能套娃的层数。
设我们当前想从 \(f_x\) 转移到其某个儿子 \(f_y\)。明显,\(f_y\) 有两种可能,一是直接就等于 \(f_x\),另一种是等于 \(f_x+1\),此时是在 \(f_x\) 外面套了一层。
我们考虑找出 \(\text{endpos}(y)\) 中任意一个元素(因为其代表的串都是相同的,所以任选一个即可),设作 \(pos_y\)。则,在 \(pos_y\) 处,\(x\) 所对应的串已经出现过一次了,只需在剩下的位置中再出现一次即可。因为其必须完整地在 \(y\) 中出现,又不与后缀的那一次重合,所以其必须在区间 \(\big[pos_y-\text{len}(y)+\text{len}(x),pos_y-1\Big]\) 中出现。换言之,\(\text{endpos}(x)\) 中,至少要有一个元素在上述区间内。
考虑用线段树合并维护 \(\text{endpos}\) 集合即可。这也是某些题中的经典套路。
代码:
#include<bits/stdc++.h>
using namespace std;
int n,cnt=1,rt[400100],f[400100],las[400100],pos[400100],res;
char s[200100];
struct Suffix_Automaton{int ch[26],len,fa;}t[400100];
int Add(int x,int c){
int xx=++cnt;t[xx].len=t[x].len+1;
for(;x&&!t[x].ch[c];x=t[x].fa)t[x].ch[c]=xx;
if(!x){t[xx].fa=1;return xx;}
int y=t[x].ch[c];
if(t[y].len==t[x].len+1){t[xx].fa=y;return xx;}
int yy=++cnt;t[yy]=t[y],t[yy].len=t[x].len+1;
t[xx].fa=t[y].fa=yy;
for(;x&&t[x].ch[c]==y;x=t[x].fa)t[x].ch[c]=yy;
return xx;
}
#define mid ((l+r)>>1)
int tot;
struct SegmentTree{
int lson,rson;
}seg[20010000];
int merge(int x,int y){
if(!x||!y)return x+y;
int z=++tot;
seg[z].lson=merge(seg[x].lson,seg[y].lson);
seg[z].rson=merge(seg[x].rson,seg[y].rson);
return z;
}
void modify(int &x,int l,int r,int P){
if(l>P||r<P)return;
if(!x)x=++tot;
if(l!=r)modify(seg[x].lson,l,mid,P),modify(seg[x].rson,mid+1,r,P);
}
bool query(int x,int l,int r,int L,int R){
if(l>R||r<L||!x)return false;
if(L<=l&&r<=R)return true;
return query(seg[x].lson,l,mid,L,R)|query(seg[x].rson,mid+1,r,L,R);
}
vector<int>v[400100];
void dfs1(int x){for(auto y:v[x])dfs1(y),rt[x]=merge(rt[x],rt[y]),pos[x]=pos[y];}
void dfs2(int x){
res=max(res,f[x]);
for(auto y:v[x]){
if(query(rt[las[x]],0,n-1,pos[y]-t[y].len+t[las[x]].len,pos[y]-1))f[y]=f[x]+1,las[y]=y;
else f[y]=f[x],las[y]=las[x];
dfs2(y);
}
}
int main(){
scanf("%d%s",&n,s);
for(int i=0,las=1;i<n;i++)las=Add(las,s[i]-‘a‘),modify(rt[las],0,n-1,i),pos[las]=i;
for(int i=2;i<=cnt;i++)v[t[i].fa].push_back(i);
dfs1(1);
for(auto y:v[1])f[y]=1,las[y]=y,dfs2(y);
printf("%d\n",res);
return 0;
}
标签:区间 %s href lan query 集合 false com ace
原文地址:https://www.cnblogs.com/Troverld/p/14605738.html