[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 
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 && 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

你可能感兴趣的:([BZOJ1396] 识别子串 - 后缀自动机,线段树)