bzoj 1396 识别子串 后缀树+线段树

题目大意

给定一个长度\(\le100000\)的字符串
求每一个位置的最短识别子串
对于位置\(x\),能识别子串\(s[i...j]\)的条件是
1.\(i\le x \le j\)
2.\(s[i...j]\)在原串中只出现了一次

分析

从第二个条件入手
仅出现一次子串就是后缀树上\(|right|=1\)的子串
考虑贡献
该后缀左端点在\(left\)
长度范围\([L,R]\)
如图
对于\(A\)部分贡献的最短串长度为\(L\)
对于\(B\)部分贡献是等差数列\(i-lef+1\),提出\(1-lef\)
对上面分两棵线段树维护即可

solution

#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <cmath>
#include <cctype>
#include <algorithm>
using namespace std;
const int M=200007;
const int INF=1e9+7;

struct edge{int y,nxt;};
struct vec{
    int g[M],te;
    edge e[M];
    vec(){memset(g,0,sizeof(g));te=0;}
    void clear(){memset(g,0,sizeof(g));te=0;}
    inline void push(int x,int y){e[++te].y=y;e[te].nxt=g[x];g[x]=te;}
    inline int& operator () (int x){return g[x];}
    inline edge& operator [] (int x){return e[x];}
}go;

char s[M];
int n;
int ch[M][26];
int stp[M],fa[M];
int lef[M];
int last,tot;
int ans[M];

struct Seg{
    int tag;
    Seg(){tag=INF;}
}seg1[262147],seg2[262147];

int newnode(int ss){
    stp[++tot]=ss;
    return tot;
}

int ext(int p,int q,int d){
    int nq=newnode(stp[p]+1);
    fa[nq]=fa[q]; fa[q]=nq;
    memcpy(ch[nq],ch[q],sizeof(ch[q]));
    for(;p&&ch[p][d]==q;p=fa[p]) ch[p][d]=nq;
    return nq;
}

int sam(int p,int d){
    int np=ch[p][d];
    if(np) return (stp[p]+1==stp[np]) ? np : ext(p,np,d);
    
    np=newnode(stp[p]+1);
    for(;p&&!ch[p][d];p=fa[p]) ch[p][d]=np;
    if(!p) fa[np]=1;
    else{
        int q=ch[p][d];
        fa[np]= (stp[p]+1==stp[q]) ? q : ext(p,q,d);
    }
    return np;
}

void mdf(Seg *S,int x,int l,int r,int tl,int tr,int d){
    if(S[x].tag<=d) return;
    if(tl<=l&&r<=tr) {S[x].tag=d;return;}
    int mid=l+r>>1;
    if(tl<=mid) mdf(S,x<<1,l,mid,tl,tr,d);
    if(mid<tr) mdf(S,x<<1|1,mid+1,r,tl,tr,d);
}

void get(Seg *S,int x,int l,int r,int nw,int kd){
    nw=min(nw,S[x].tag);
    if(l==r){
        if(kd==0) ans[l]=min(ans[l],nw);
        else ans[l]=min(ans[l],l+nw);
        return;
    }
    int mid=l+r>>1;
    get(S,x<<1,l,mid,nw,kd);
    get(S,x<<1|1,mid+1,r,nw,kd);
}

void dfs(int x){
    int p,y;
    for(p=go(x);p;p=go[p].nxt){
        y=go[p].y;
        dfs(y);
        if(lef[y]){
            if(lef[x]==0) lef[x]=lef[y];
            else if(lef[x]>0) lef[x]=-1;
        }
    }
    if(lef[x]>0){//if only one lef
        int L=stp[fa[x]]+1;
        int R=stp[x];
        mdf(seg1,1,1,n,lef[x],lef[x]+L-1, L);
        if(L!=R) mdf(seg2,1,1,n,lef[x]+L,lef[x]+R-1, 1-lef[x]);
    }
}

int main(){
    
    int i;
    scanf("%s",s+1);
    n=strlen(s+1);
    
    last=tot=1;
    for(i=n;i>0;i--){
        last=sam(last,s[i]-'a');
        lef[last]=i;
    }
    
    for(i=2;i<=tot;i++) go.push(fa[i],i);
    dfs(1);
    
    for(i=1;i<=n;i++) ans[i]=INF;
    get(seg1,1,1,n,INF,0);
    get(seg2,1,1,n,INF,1);
    for(i=1;i<=n;i++) printf("%d\n",ans[i]);
    
    return 0;
}

转载于:https://www.cnblogs.com/acha/p/6592853.html

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
毕业设计,基于SpringBoot+Vue+MySQL开发的公寓报修管理系统,源码+数据库+毕业论文+视频演示 现代经济快节奏发展以及不断完善升级的信息化技术,让传统数据信息的管理升级为软件存储,归纳,集中处理数据信息的管理方式。本公寓报修管理系统就是在这样的大环境下诞生,其可以帮助管理者在短时间内处理完毕庞大的数据信息,使用这种软件工具可以帮助管理人员提高事务处理效率,达到事半功倍的效果。此公寓报修管理系统利用当下成熟完善的Spring Boot框架,使用跨平台的可开发大型商业网站的Java语言,以及最受欢迎的RDBMS应用软件之一的MySQL数据库进行程序开发。公寓报修管理系统有管理员,住户,维修人员。管理员可以管理住户信息和维修人员信息,可以审核维修人员的请假信息,住户可以申请维修,可以对维修结果评价,维修人员负责住户提交的维修信息,也可以请假。公寓报修管理系统的开发根据操作人员需要设计的界面简洁美观,在功能模块布局上跟同类型网站保持一致,程序在实现基本要求功能时,也为数据信息面临的安全问题提供了一些实用的解决方案。可以说该程序在帮助管理者高效率地处理工作事务的同时,也实现了数据信息的整体化,规范化与自动化。 关键词:公寓报修管理系统;Spring Boot框架;MySQL;自动化;VUE
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值