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

对于一个子串,如果其长度不小于相邻后缀距离的最大值,且最靠后的位置加上最长匹配的后缀长度不小于$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;
}

  

BZOJ3499 : PA2009 Quasi-template的更多相关文章

  1. 为.NET Core项目定义Item Template

    作为这个星球上最强大的IDE,Visual Studio不仅仅提供了很多原生的特性,更重要的是它是一个可定制的IDE,比如自定义Project Template和Item Template就是一个非常 ...

  2. jQuery.template.js 简单使用

    之前看了一篇文章<我们为什么要尝试前后端分离>,深有同感,并有了下面的评论: 我最近也和前端同事在讨论这个问题,比如有时候前端写好页面给后端了,然后后端把这些页面拆分成很多的 views, ...

  3. 2000条你应知的WPF小姿势 基础篇<69-73 WPF Freeze机制和Template>

    在正文开始之前需要介绍一个人:Sean Sexton. 来自明尼苏达双城的软件工程师.最为出色的是他维护了两个博客:2,000ThingsYou Should Know About C# 和 2,00 ...

  4. tornado template

    若果使用Tornado进行web开发可能会用到模板功能,页面继承,嵌套... 多页应用模板的处理多半依赖后端(SPA就可以动态加载局部视图),就算是RESTfull的API设计,也不妨碍同时提供部分模 ...

  5. 设计模式(九): 从醋溜土豆丝和清炒苦瓜中来学习"模板方法模式"(Template Method Pattern)

    今天是五.四青年节,祝大家节日快乐.看着今天这标题就有食欲,夏天到了,醋溜土豆丝和清炒苦瓜适合夏天吃,好吃不上火.这两道菜大部分人都应该吃过,特别是醋溜土豆丝,作为“鲁菜”的代表作之一更是为大众所熟知 ...

  6. C++泛型编程:template模板

    泛型编程就是以独立于任何特定类型的方式编写代码,而模板是C++泛型编程的基础. 所谓template,是针对“一个或多个尚未明确的类型”所编写的函数或类. 使用template时,可以显示的或隐示的将 ...

  7. 新手入门Underscore.js 中文(template)

    Underscore.js是一个很精干的库,压缩后只有4KB.它提供了几十种函数式编程的方法,弥补了标准库的不足,大大方便了javaScript的编程.MVC框架Backbone.js就将这个库作为自 ...

  8. knockoutjs如何动态加载外部的file作为component中的template数据源

    玩过knockoutjs的都知道,有一个强大的功能叫做component,而这个component有个牛逼的地方就是拥有自己的viewmodel和template, 比如下面这样: ko.compon ...

  9. JavaScript模板引擎artTemplate.js——template.helper()方法

    上一篇文章我们已经讲到了helper()方法,但是上面的例子只是一个参数的写法,如果是多个参数,写法就另有区别了. <div id="user_info"></d ...

随机推荐

  1. Centos7上配置网络和本地yum方法

    配置网络yum源 前提:1.这个系统能上网 2.vim /etc/resolv.conf nameserver 8.8.8.8 nameserver 114.114.114.114 操作如下: 1.m ...

  2. python杂写

    一:用户交互 与用户交互主要使用input,这里需要说明三点: 1:input会等待用户输入 2:会将输入的内容赋值给变量 3:input出的变量都是字符串类型(str) 例子1:注意,因为input ...

  3. 常见的爬虫分析库(4)-爬虫之PyQuery

    PyQuery 是 Python 仿照 jQuery 的严格实现.语法与 jQuery 几乎完全相同. 官方文档:http://pyquery.readthedocs.io/ 安装 1 pip ins ...

  4. (转载)dotnet core 中文乱码 codepages

    引子 转载自:http://www.jianshu.com/p/1c9c59c5749a 参考:.Net Core 控制台输出中文乱码 上文中我查阅了一些cli的源码, 闲来无事就继续翻代码, 冥冥之 ...

  5. jenkins X实践系列(4) —— jenkins X 构建提速

    jx是云原生CICD,devops的一个最佳实践之一,目前在快速的发展成熟中.最近调研了JX,这里为第4篇,介绍如何加入jx构建和部署. builder镜像下载慢 先在一台机器上下载好,然后放到本地仓 ...

  6. Web Deploy发布网站错误 检查授权和委派设置

    web Deploy发布asp.net网站给我们提供方便,开始配置好了可以方便的发布网站,但是过久就出现无法执行此操作.请与服务器管理员联系,检查授权和委派设置.花了好长时间找到问问所在.现在解决方法 ...

  7. Linux 记录所有用户登录和操作的详细日志

    1.起因 最近Linux服务器上一些文件呗篡改,想追查已经查不到记录了,所以得想个办法记录下所有用户的操作记录. 一般大家通常会采用history来记录,但是history有个缺陷就是默认是1000行 ...

  8. Python 多进程multiprocessing

    一.python多线程其实在底层来说只是单线程,因此python多线程也称为假线程,之所以用多线程的意义是因为线程不停的切换这样比串行还是要快很多.python多线程中只要涉及到io或者sleep就会 ...

  9. JavaMelody 项目性能监控和调优工具

    转自 JavaMelody 可以实现对内存.CPU.线程.JDBC 连接数.HTTP 请求执行时间.SQL 执行时间(分析 Top SQL).方法执行时间(分析系统方法性能瓶颈)等等的监控. 配置方式 ...

  10. 20165235 祁瑛 2018-4 《Java程序设计》第六周学习总结

    20165235 祁瑛 2018-4 <Java程序设计>第六周学习总结 教材学习内容总结 常用实用类 (一)1.String类是final类型的,String类型常量也是对象.2.可以使 ...