BZOJ3499 : PA2009 Quasi-template
建立后缀树,用线段树合并求出每个节点子树内部最靠前和最靠后的后缀位置以及相邻后缀距离的最大值,同时求出每个子串能完整匹配的最长后缀的长度。
对于一个子串,如果其长度不小于相邻后缀距离的最大值,且最靠后的位置加上最长匹配的后缀长度不小于$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的更多相关文章
- 为.NET Core项目定义Item Template
作为这个星球上最强大的IDE,Visual Studio不仅仅提供了很多原生的特性,更重要的是它是一个可定制的IDE,比如自定义Project Template和Item Template就是一个非常 ...
- jQuery.template.js 简单使用
之前看了一篇文章<我们为什么要尝试前后端分离>,深有同感,并有了下面的评论: 我最近也和前端同事在讨论这个问题,比如有时候前端写好页面给后端了,然后后端把这些页面拆分成很多的 views, ...
- 2000条你应知的WPF小姿势 基础篇<69-73 WPF Freeze机制和Template>
在正文开始之前需要介绍一个人:Sean Sexton. 来自明尼苏达双城的软件工程师.最为出色的是他维护了两个博客:2,000ThingsYou Should Know About C# 和 2,00 ...
- tornado template
若果使用Tornado进行web开发可能会用到模板功能,页面继承,嵌套... 多页应用模板的处理多半依赖后端(SPA就可以动态加载局部视图),就算是RESTfull的API设计,也不妨碍同时提供部分模 ...
- 设计模式(九): 从醋溜土豆丝和清炒苦瓜中来学习"模板方法模式"(Template Method Pattern)
今天是五.四青年节,祝大家节日快乐.看着今天这标题就有食欲,夏天到了,醋溜土豆丝和清炒苦瓜适合夏天吃,好吃不上火.这两道菜大部分人都应该吃过,特别是醋溜土豆丝,作为“鲁菜”的代表作之一更是为大众所熟知 ...
- C++泛型编程:template模板
泛型编程就是以独立于任何特定类型的方式编写代码,而模板是C++泛型编程的基础. 所谓template,是针对“一个或多个尚未明确的类型”所编写的函数或类. 使用template时,可以显示的或隐示的将 ...
- 新手入门Underscore.js 中文(template)
Underscore.js是一个很精干的库,压缩后只有4KB.它提供了几十种函数式编程的方法,弥补了标准库的不足,大大方便了javaScript的编程.MVC框架Backbone.js就将这个库作为自 ...
- knockoutjs如何动态加载外部的file作为component中的template数据源
玩过knockoutjs的都知道,有一个强大的功能叫做component,而这个component有个牛逼的地方就是拥有自己的viewmodel和template, 比如下面这样: ko.compon ...
- JavaScript模板引擎artTemplate.js——template.helper()方法
上一篇文章我们已经讲到了helper()方法,但是上面的例子只是一个参数的写法,如果是多个参数,写法就另有区别了. <div id="user_info"></d ...
随机推荐
- exec函数族
进程程序替换 进程程序替换原理 fork创建子进程执行的是和父进程相同的程序(也有可能是某个分支),通常fork出的子进程是为了完成父进程所分配的任务,所以子进程通常会调用一种exec函数(六种中的任 ...
- 用Photoshop扭曲滤镜工具打造旋转的光束效果
原文:https://www.w3cschool.cn/photoshopgjjc/dthfn1.html 教程与已往的不同,作者加上了一些新的步骤如锐化等,出来的效果也有所改进.大致过程:先用滤镜制 ...
- Squid作代理服务器,用户密码验证,高匿代理
参考URL: https://www.cnblogs.com/vijayfly/p/5800038.html https://www.cnblogs.com/operaculus/p/5705184. ...
- webpack学习笔记--其它配置项
其它配置项 除了前面介绍到的配置项外,Webpack 还提供了一些零散的配置项.下面来介绍它们中常用的部分. Target JavaScript 的应用场景越来越多,从浏览器到 Node.js,这些 ...
- JavaScript数组去重的6个方法
方法一无需思考,我们可以得到 O(n^2) 复杂度的解法.定义一个变量数组 res 保存结果,遍历需要去重的数组,如果该元素已经存在在 res 中了,则说明是重复的元素,如果没有,则放入 res 中. ...
- ionic 3 安卓手机获取经纬度坐标
现在有个需求:每隔一段时间需向后台服务器返回当前用户的经纬度坐标. ionic 官方提供的有定位插件cordova-plugin-geolocation,兼容ios和android版本,网上查资料说最 ...
- WinRAR 0day漏洞
WinRAR 0day漏洞(附利用过程) 英国安全机构Mohammad Reza Espargham的漏洞实验室发现,流行压缩工具WinRAR 5.21最新版里存在一个安全漏洞,目前该漏洞还属于零日漏 ...
- Hive| DDL| DML
类型转换 可以使用CAST操作显示进行数据类型转换 例如CAST(' 转换成整数1:如果强制类型转换失败,如执行CAST('X' AS INT),表达式返回空值 NULL. : jdbc:hive2: ...
- TCP简介
TCP(Transmission Control Protocol) 传输控制协议,是一种面向连接的.可靠的.基于字节流的传输层通信协议. TCP是一种面向连接(连接导向)的.可靠的基于字节流的传输层 ...
- 20165235 实验三 敏捷开发与XP实践
20165235 实验三 敏捷开发与XP实践 主目录: 姓名:祁瑛 学号:20165235 班级:1652 实验课程:JAVA程序设计 实验名称:Java面向对象程序设计 实验时间:2018.4.30 ...