bzoj1396 识别子串

题面
https://www.lydsy.com/JudgeOnline/problem.php?id=1396
首先,对于题目所说的T在S中只出现一次这个要求,
可以利用后缀自动机的parent树的叶子节点的性质来满足要求。
显然所有满足T在S中只出现过一次的子串就是parent树所有叶子节点的right集合的并集。
考虑怎么去找到最小值。
对于一个叶子节点来说,其right集合中所有串的右端点显然是一样的,左端点依次向右,长度依次递减,直到左端点不再属于这个点的right集合位置。
可以用一个式子来描述rifht集合的左端点的范围【id[x]-s[x].len+1,id[x]-s[s[x].link].len】。
进一步地发现这个right集合对答案的贡献是一条斜线和一条水平线(这里所说的贡献是使答案对其取min)
因为对于【id[x]-s[x].len+1,id[x]-s[s[x].link].len】可以产生的贡献随着长度依次递减,是一条斜线。
而对于【id[x]-s[s[x].link].len+1,id[x]】,由于左端点位于这个位置时,不再满足“T在S中只出现过一次”这一要求。
因此可以产生的贡献只能是刚才那条斜线的右端点的水平延长线。
分析完操作后,考虑怎么实现这两个区间操作。
有一个自闭的想法是直接强行上一个李超线段树就完事了,然而并没有必要。
发现由于只关心最后的答案,可以离线,即把斜线和水平线分开做,跑两次,分别用不同的线段树维护,这样就simple了很多。

#include<iostream>
#include<cctype>
#include<cstdio>
#include<cstring>
#include<string>
#include<cmath>
#include<ctime>
#include<cstdlib>
#include<algorithm>
#define N 330000
#define L 300000
#define eps 1e-7
#define inf 1e9+7
#define ll long long
using namespace std;
inline int read()
{
    char ch=0;
    int x=0,flag=1;
    while(!isdigit(ch)){ch=getchar();if(ch=='-')flag=-1;}
    while(isdigit(ch)){x=(x<<3)+(x<<1)+ch-'0';ch=getchar();}
    return x*flag;
}
char ch[N];
bool flag[N];
int root=1,size=1,last=1,id[N];
struct node{int len,link,nxt[26];}s[N];
void insert(int k)
{
    int cur=++size,p=last;
    s[cur].len=s[p].len+1;
    while(p&&!s[p].nxt[k])
    {
        s[p].nxt[k]=cur;
        p=s[p].link;
    }
    last=cur;
    if(!p){s[cur].link=root;return;}
    int q=s[p].nxt[k];
    if(s[q].len==s[p].len+1)s[cur].link=q;
    else
    {
        int clone=++size;
        s[clone]=s[q];
        s[clone].len=s[p].len+1;
        while(p&&s[p].nxt[k]==q)
        {
            s[p].nxt[k]=clone;
            p=s[p].link;
        }
        s[q].link=s[cur].link=clone;
    }
}
struct Segment_Tree
{
    #define lson o<<1
    #define rson o<<1|1
    #define mid ((l+r)>>1)
    int num,setv[N*4];
    inline void pushdown(int o,int l,int r)
    {
        if(setv[o]==inf)return;
        setv[lson]=min(setv[lson],setv[o]);
        setv[rson]=min(setv[rson],setv[o]-(mid-l+1));
        setv[o]=inf;
    }
    void build(int o,int l,int r)
    {
        setv[o]=inf;
        if(l==r)return;
        build(lson,l,mid);
        build(rson,mid+1,r);
    }
    void optset(int o,int l,int r,int ql,int qr)
    {
        if(ql<=l&&r<=qr)
        {
            setv[o]=min(setv[o],num);
            num-=r-l+1;
            return;
        }
        pushdown(o,l,r);
        if(ql<=mid)optset(lson,l,mid,ql,qr);
        if(qr>mid)optset(rson,mid+1,r,ql,qr);
        return;
    }
    void rebuild(int o,int l,int r)
    {
        if(l==r)return;
        pushdown(o,l,r);
        rebuild(lson,l,mid);
        rebuild(rson,mid+1,r);
        setv[o]=inf;
    }
    inline void pushdown_(int o)
    {
        if(setv[o]==inf)return;
        setv[lson]=min(setv[lson],setv[o]);
        setv[rson]=min(setv[rson],setv[o]);
        setv[o]=inf;
    }
    void optset_(int o,int l,int r,int ql,int qr)
    {
        if(ql<=l&&r<=qr)
        {
            setv[o]=min(setv[o],num);
            return;
        }
        pushdown_(o);
        if(ql<=mid)optset_(lson,l,mid,ql,qr);
        if(qr>mid)optset_(rson,mid+1,r,ql,qr);
        return;
    }
    void print(int o,int l,int r)
    {
        if(l==r)
        {
            printf("%d\n",setv[o]);
            return;
        }
        pushdown_(o);
        print(lson,l,mid);
        print(rson,mid+1,r);
    }
}T;
int main()
{
    int n;
    scanf("%s",ch);n=strlen(ch);
    for(int i=0;i<n;i++)insert(ch[i]-'a'),id[last]=i;
    T.build(1,0,n-1);
    for(int x=1;x<=size;x++)flag[s[x].link]=true;
    for(int x=1;x<=size;x++)if(!flag[x])
    {
        int l=id[x]-s[x].len+1;
        int r=id[x]-s[s[x].link].len;
        T.num=s[x].len;
        T.optset(1,0,n-1,l,r);
    }
    T.rebuild(1,0,n-1);
    for(int x=1;x<=size;x++)if(!flag[x])
    {
        int l=id[x]-s[s[x].link].len+1;
        int r=id[x];
        T.num=s[s[x].link].len+1;
        T.optset_(1,0,n-1,l,r);
    }
    T.print(1,0,n-1);
    return 0;
}

转载于:https://www.cnblogs.com/Creed-qwq/p/10149036.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值