bzoj 1396 识别子串

博客详细介绍了如何解决bzoj 1396问题,即找出给定字符串中每个字符的最短识别子串。通过对样例输入和输出的展示,解释了如何通过考虑只出现一次的串作为前缀,并使用线段树来分别计算不同部分的贡献来求解问题。
摘要由CSDN通过智能技术生成

http://www.elijahqi.win/archives/3786
Description

Input
一行,一个由小写字母组成的字符串S,长度不超过10^5
Output
L行,每行一个整数,第i行的数据表示关于S的第i个元素的最短识别子串有多长.
Sample Input
agoodcookcooksgoodfood
Sample Output
1
2
3
3
2
2
3
3
2
2
3
3
2
1
2
3
3
2
1
2
3
4
HINT

Source
考虑一个只i出现一次的串一定是一个前缀串

那么对答案的贡献分成两部分 一部分是len[x]-fa~len[x]的这部分贡献会是r-l+1因为只要多构前面一个字母就可以形成最小的 剩下一部分1~(len-fa)-1这部分 会和i~r构成答案 两棵线段树分开计数即可

#include<bits/stdc++.h>
#define lc (x<<1)
#define rc (x<<1|1)
using namespace std;
const int N=1e5+10;
const int inf=0x3f3f3f3f;
int cnt=1,last=1,root=1,ch[N<<1][26],len[N<<1];
char s[N];int fa[N<<1],size[N<<1],n;
struct node{
    int mn[N<<2];
    inline void init(){memset(mn,0x3f,sizeof(mn));}
    inline void pushdown(int x){
        if (mn[x]==inf) return;
        mn[lc]=min(mn[lc],mn[x]);mn[rc]=min(mn[rc],mn[x]);mn[x]=inf;
    }
    inline void modify(int x,int l,int r,int l1,int r1,int v){
        if (l1<=l&&r1>=r){mn[x]=min(mn[x],v);return;}
        int mid=l+r>>1;pushdown(x);
        if (l1<=mid) modify(lc,l,mid,l1,r1,v);
        if (r1>mid) modify(rc,mid+1,r,l1,r1,v);
    }
    inline int query(int x,int l,int r,int p){
        if (l==r) return mn[x];
        int mid=l+r>>1;pushdown(x);
        if(p<=mid) return query(lc,l,mid,p);
        else return query(rc,mid+1,r,p);
    }
}tree1,tree2;
inline void insert1(int x){
    int p=last,np=++cnt;len[np]=len[p]+1;size[np]=1;
    for (;p&&!ch[p][x];p=fa[p]) ch[p][x]=np;
    if (!p) fa[np]=root;else{
        int q=ch[p][x];
        if (len[p]+1==len[q]) fa[np]=q;else{
            int nq=++cnt;fa[nq]=fa[q];memcpy(ch[nq],ch[q],sizeof(ch[q]));
            fa[np]=fa[q]=nq;len[nq]=len[p]+1;
            for (;p&&ch[p][x]==q;p=fa[p]) ch[p][x]=nq;
        }
    }last=np;
}
int main(){
    freopen("bzoj1396.in","r",stdin);
    scanf("%s",s+1);n=strlen(s+1);
    for (int i=1;i<=n;++i) insert1(s[i]-'a');
    for (int i=1;i<=cnt;++i) size[i]=1;
    for (int i=1;i<=cnt;++i) size[fa[i]]=0;
    tree1.init();tree2.init();
    for (int i=1;i<=cnt;++i){
        if (size[i]!=1) continue;
        int l=len[i]-len[fa[i]],r=len[i];
        if(1<=l-1) tree1.modify(1,1,n,1,l-1,r+1);
        tree2.modify(1,1,n,l,r,r-l+1);
    }
    for (int i=1;i<=n;++i)
        printf("%d\n",min(tree1.query(1,1,n,i)-i,tree2.query(1,1,n,i)));
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值