zoukankan      html  css  js  c++  java
  • [BZOJ1396] 识别子串

    Description

    问字符串 (S) 每一位的最短识别子串是多长(识别子串指包含这个字符且只出现在 (S) 中一次的子串)。

    Solution

    首先建出 SAM,由于我们要求出现次数为 (1),只有 (endpos) 集合大小为 (1) 的那些结点有贡献

    满足这个条件的结点所表示的串的集合一定是 ([i,pos]),其中 (pos) 为定值,(i in [pos-maxlen+1, pos-minlen+1])

    于是这个节点对 ([pos-minlen+1,pos]) 中的每个点产生 (minlen) 的贡献,对 ([pos-maxlen+1,pos-minlen]) 中的每个点 (i) 产生 (pos-i+1) 的贡献

    对于第一部分,线段树直接维护(区间求 (min))即可

    对于第二部分,线段树维护 (f[i]-i) 即可(这样修改的值就是 (pos+1)

    #include <bits/stdc++.h>
    using namespace std;
    const int Maxn = 2000005;
    const int N = 2000005;
    
    struct seg
    {
        int a[N];
        seg()
        {
            memset(a,0x3f,sizeof a);
        }
        void modify(int p,int l,int r,int ql,int qr,int x)
        {
            if(l>qr || r<ql) return;
            if(l>=ql && r<=qr)
            {
                a[p]=min(a[p],x);
            }
            else
            {
                modify(p*2,l,(l+r)/2,ql,qr,x);
                modify(p*2+1,(l+r)/2+1,r,ql,qr,x);
            }
        }
        int query(int p,int l,int r,int pos)
        {
            if(l==r) return a[p];
            if(pos<=(l+r)/2) return min(a[p],query(p*2,l,(l+r)/2,pos));
            else return min(a[p],query(p*2+1,(l+r)/2+1,r,pos));
        }
    } seg1,seg2;
    
    namespace sam {
        int maxlen[Maxn], minlen[Maxn], trans[Maxn][26], link[Maxn], Size=1, Last=1;
        int t[Maxn], a[Maxn], cnt[Maxn], f[Maxn], ep[Maxn];
        inline void Extend(int id, int le) {
            int cur = (++ Size), p;
            maxlen[cur] = maxlen[Last] + 1;
            ep[cur] = le;
            cnt[cur] = 1;
            for (p = Last; p && !trans[p][id]; p = link[p]) trans[p][id] = cur;
            if (!p) link[cur] = 1;
            else {
                int q = trans[p][id];
                if (maxlen[q] == maxlen[p] + 1) link[cur] = q;
                else {
                    int clone = (++ Size);
                    maxlen[clone] = maxlen[p] + 1;
                    ep[clone] = ep[q];
                    for(int i=0;i<26;i++) trans[clone][i] = trans[q][i];
                    link[clone] = link[q];
                    for (; p && trans[p][id] == q; p = link[p]) trans[p][id] = clone;
                    link[cur] = link[q] = clone;
                }
            }
            Last = cur;
        }
        void CalcEndposSize() {
            memset(t, 0, sizeof t);
            for(int i=1; i<=Size; i++) t[maxlen[i]]++;
            for(int i=1; i<=Size; i++) t[i]+=t[i-1];
            for(int i=1; i<=Size; i++) a[t[maxlen[i]]--]=i;
            for(int i=Size; i>=1; --i) cnt[link[a[i]]]+=cnt[a[i]];
            cnt[1] = 0;
    
            for(int i=1; i<=Size; i++) minlen[i]=maxlen[link[i]]+1;
        }
    }
    
    int main() {
        ios::sync_with_stdio(false);
        string str;
        cin>>str;
        int n=str.length();
        int t,k;
        for(int i=0;i<str.length();i++)
            sam::Extend(str[i]-'a',i+1);
        sam::CalcEndposSize();
        using sam::maxlen;
        using sam::minlen;
        using sam::ep;
        using sam::cnt;
        using sam::Size;
    
        for(int i=1;i<=Size;i++)
        {
            if(cnt[i]==1)
            {
                int pos=ep[i];
                seg1.modify(1,1,n,pos-minlen[i]+1,pos,minlen[i]);
                seg2.modify(1,1,n,pos-maxlen[i]+1,pos-minlen[i],pos+1);
            }
        }
        for(int i=1;i<=n;i++)
        {
            cout<<min(seg1.query(1,1,n,i),seg2.query(1,1,n,i)-i)<<endl;
        }
    }
    
  • 相关阅读:
    Android 自定义android控件EditText边框背景
    Android安全问题 静音拍照与被拍
    Android 自绘TextView解决提前换行问题,支持图文混排
    Android EditText属性
    Android invalidate()自动清屏,屏幕刷新
    Cocos2d-x 3.0final 终结者系列教程12-Vector&amp;map&amp;value
    思维方式--SMART原则
    从.net复制源代码中国农历阵列,必要做日历
    POJ 3071-Football(可能性dp)
    mongodb group包(最具体的、最受欢迎、最容易理解的解释)
  • 原文地址:https://www.cnblogs.com/mollnn/p/13282787.html
Copyright © 2011-2022 走看看