首页 技术 正文
技术 2022年11月20日
0 收藏 361 点赞 4,081 浏览 4235 个字

建立后缀树,用线段树合并求出每个节点子树内部最靠前和最靠后的后缀位置以及相邻后缀距离的最大值,同时求出每个子串能完整匹配的最长后缀的长度。

对于一个子串,如果其长度不小于相邻后缀距离的最大值,且最靠后的位置加上最长匹配的后缀长度不小于$n$,那么就说明可以从中间开始覆盖到尾部。

对串做KMP,求出每个前缀的最长border,也就是$nxt$数组。

对于一个子串,设其最早出现的位置为$[l,r]$,那么只要$nxt[r]\geq l-1$,就说明可以覆盖头部。

因为后缀树边经过压缩,所以不能直接枚举所有子串。

对于计数,可以离线之后扫描线树状数组处理。

对于长度最小且字典序最小的解,可以考虑用线段树维护区间内$nxt$的最大值,然后在线段树上二分。

时间复杂度$O(n\log n)$。

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cstdlib>
using namespace std;
const int inf=1<<30,S=27,N=200010;
int root,last,pos,need,remain,acnode,ace,aclen;
int n,i,j,nxt[N],f[N<<1],q[N],cnt,first,minl=inf,ansl,ansr;
char text[N],a[N];long long ans;
struct node{int st,en,lk,son[S];int len(){return min(en,pos+1)-st;}}tree[N<<1];
struct E{int x,l,r;E(){}E(int _x,int _l,int _r){x=_x,l=_l,r=_r;}}e[N<<1];
inline bool cmp(int x,int y){return nxt[x]>nxt[y];}
inline bool cmpe(const E&a,const E&b){return a.x>b.x;}
namespace DS{
const int M=3800000;
int tot,l[M],r[M],v[M],vl[M],vr[M],T[N<<1];
int build(int a,int b,int c){
int x=++tot;
vl[x]=vr[x]=c;
if(a==b)return tot;
int mid=(a+b)>>1;
if(c<=mid)l[x]=build(a,mid,c);else r[x]=build(mid+1,b,c);
return x;
}
int merge(int x,int y,int a,int b){
if(!x||!y)return x+y;
int mid=(a+b)>>1;
l[x]=merge(l[x],l[y],a,mid);
r[x]=merge(r[x],r[y],mid+1,b);
vl[x]=l[x]?vl[l[x]]:vl[r[x]];
vr[x]=r[x]?vr[r[x]]:vr[l[x]];
v[x]=max(v[l[x]],v[r[x]]);
if(l[x]&&r[x])v[x]=max(v[x],vl[r[x]]-vr[l[x]]);
return x;
}
}
namespace RangeQuery{
int v[524300],bit[N];
void build(int x,int a,int b){
if(a==b){
v[x]=nxt[a];
return;
}
int mid=(a+b)>>1;
build(x<<1,a,mid),build(x<<1|1,mid+1,b);
v[x]=max(v[x<<1],v[x<<1|1]);
}
void get(int x,int a,int b,int c,int d,int p){
if(first<inf||v[x]<p)return;
if(a==b)first=a;
int mid=(a+b)>>1;
if(c<=mid)get(x<<1,a,mid,c,d,p);
if(d>mid)get(x<<1|1,mid+1,b,c,d,p);
}
inline void add(int x){for(x++;x<=n;x+=x&-x)bit[x]++;}
inline int ask(int x){int t=0;for(;x;x-=x&-x)t+=bit[x];return t;}
inline int sum(int l,int r){return ask(r+1)-ask(l);}
}
int new_node(int st,int en=inf){
node nd;
nd.st=st;nd.en=en;
for(int i=nd.lk=0;i<S;i++)nd.son[i]=0;
tree[++last]=nd;
return last;
}
char acedge(){return text[ace];}
void addedge(int node){
if(need)tree[need].lk=node;
need=node;
}
bool down(int node){
if(aclen>=tree[node].len())return ace+=tree[node].len(),aclen-=tree[node].len(),acnode=node,1;
return 0;
}
void init(){
need=last=remain=ace=aclen=0;
root=acnode=new_node(pos=-1,-1);
}
void extend(char c){
text[++pos]=c;need=0;remain++;
while(remain){
if(!aclen)ace=pos;
if(!tree[acnode].son[acedge()])tree[acnode].son[acedge()]=new_node(pos),addedge(acnode);
else{
int nxt=tree[acnode].son[acedge()];
if(down(nxt))continue;
if(text[tree[nxt].st+aclen]==c){aclen++;addedge(acnode);break;}
int split=new_node(tree[nxt].st,tree[nxt].st+aclen);
tree[acnode].son[acedge()]=split;
tree[split].son[c]=new_node(pos);
tree[nxt].st+=aclen;
tree[split].son[text[tree[nxt].st]]=nxt;
addedge(split);
}
remain--;
if(acnode==root&&aclen)aclen--,ace=pos-remain+1;
else acnode=tree[acnode].lk?tree[acnode].lk:root;
}
}
void dfs(int x,int y,int sum){
sum+=tree[x].len();
if(sum&&min(tree[x].en,pos+1)==pos+1){
if(!tree[x].len())f[y]=max(f[y],sum);
else f[x]=sum;
}
for(int i=0;i<S;i++){
int u=tree[x].son[i];
if(!u)continue;
dfs(u,x,sum);
}
}
inline void solve(int l,int r,int x,int y,int ml){
l=max(l,max(DS::v[DS::T[x]],n-ml-DS::vr[DS::T[x]]));
if(l>r)return;
x=DS::vl[DS::T[x]];
if(l+10>r){
for(int i=l;i<=r;i++)if(nxt[y+i-1]>=x){
ans++;
if(i<minl)minl=i,ansl=y,ansr=y+i-1;
}
return;
}
first=inf;
RangeQuery::get(1,0,n,y+l-1,y+r-1,x);
if(first==inf)return;
first-=y-1;
if(first<minl)minl=first,ansl=y,ansr=y+first-1;
e[cnt++]=E(x,y+l-1,y+r-1);
}
void dfs2(int x,int y,int sum){
int l=sum+1;
sum+=tree[x].len();
if(sum&&min(tree[x].en,pos+1)==pos+1)DS::T[x]=DS::build(0,n,pos-sum+1);
for(int i=0;i<S;i++){
int u=tree[x].son[i];
if(!u)continue;
f[u]=max(f[u],f[x]);
dfs2(u,x,sum);
DS::T[x]=DS::merge(DS::T[x],DS::T[u],0,n);
}
if(l<=sum){
solve(l,sum-1,x,tree[x].st-l+1,f[y]);
solve(sum,sum,x,tree[x].st-l+1,f[x]);
}
}
int main(){
init();
scanf("%s",a);
n=strlen(a);
for(nxt[0]=j=-1,i=1;i<n;nxt[i++]=j){
while(~j&&a[j+1]!=a[i])j=nxt[j];
if(a[j+1]==a[i])j++;
}
for(i=0;i<n;i++)nxt[i]++,q[i]=i;
RangeQuery::build(1,0,n);
for(i=0;i<n;i++)extend(a[i]-'a');extend(26);
pos--;
dfs(root,0,0);
dfs2(root,0,0);
sort(q,q+n,cmp);
if(cnt>1)sort(e,e+cnt,cmpe);
for(i=j=0;i<cnt;i++){
while(j<n&&nxt[q[j]]>=e[i].x)RangeQuery::add(q[j++]);
ans+=RangeQuery::sum(e[i].l,e[i].r);
}
printf("%lld\n",ans);
for(i=ansl;i<=ansr;i++)putchar(a[i]);
return 0;
}

  

相关推荐
python开发_常用的python模块及安装方法
adodb:我们领导推荐的数据库连接组件bsddb3:BerkeleyDB的连接组件Cheetah-1.0:我比较喜欢这个版本的cheeta…
日期:2022-11-24 点赞:878 阅读:9,077
Educational Codeforces Round 11 C. Hard Process 二分
C. Hard Process题目连接:http://www.codeforces.com/contest/660/problem/CDes…
日期:2022-11-24 点赞:807 阅读:5,552
下载Ubuntn 17.04 内核源代码
zengkefu@server1:/usr/src$ uname -aLinux server1 4.10.0-19-generic #21…
日期:2022-11-24 点赞:569 阅读:6,401
可用Active Desktop Calendar V7.86 注册码序列号
可用Active Desktop Calendar V7.86 注册码序列号Name: www.greendown.cn Code: &nb…
日期:2022-11-24 点赞:733 阅读:6,176
Android调用系统相机、自定义相机、处理大图片
Android调用系统相机和自定义相机实例本博文主要是介绍了android上使用相机进行拍照并显示的两种方式,并且由于涉及到要把拍到的照片显…
日期:2022-11-24 点赞:512 阅读:7,813
Struts的使用
一、Struts2的获取  Struts的官方网站为:http://struts.apache.org/  下载完Struts2的jar包,…
日期:2022-11-24 点赞:671 阅读:4,896