BZOJ 1396 识别子串

题面

Description

20170701092835_34869.jpg

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

题解

后缀自动机跑一遍, 得到的Parent Tree上, 存在每个节点的\(size\)即为它所代表的子串的出现次数.
找到每一个叶子节点, 得到它所代表的最短字符串\(str[L .. R]\), 开两棵线段树, 一棵更新\([L .. R]\)区间中的位置的最短识别子串长度, 另一颗更新\([0, L - 1]\)位置上的最短识别子串.

#include <cstdio>
#include <cstring>
#include <algorithm>

const int LEN = (int)1e5;

struct segmentTree
{
    int a[LEN << 2];
    
    inline segmentTree()
    {
        memset(a, 127, sizeof(a));
    }

    void modify(int u, int curL, int curR, int L, int R, int w)
    {
        if(curL >= L && curR <= R)
        {
            a[u] = std::min(a[u], w);
            return;
        }
        int mid = curL + curR >> 1;
        if(L <= mid)
            modify(u << 1, curL, mid, L, R, w);
        if(R > mid)
            modify(u << 1 | 1, mid + 1, curR, L, R, w);
    }

    void modify(int L, int R, int w)
    {
        modify(1, 0, LEN - 1, L, R, w);
    }

    int query(int u, int L, int R, int pos)
    {
        if(L == R)
            return a[u];
        int mid = L + R >> 1;
        if(pos <= mid)
            return std::min(a[u], query(u << 1, L, mid, pos));
        else
            return std::min(a[u], query(u << 1 | 1, mid + 1, R, pos));
    }

    inline int query(int pos)
    {
        return query(1, 0, LEN - 1, pos);
    }
}A, B;

struct suffixAutomaton
{
    struct state
    {
        state *suc[26], *pre;
        int len, sz, tg;

        inline state()
        {
            sz = 1, tg = 0;
            for(int i = 0; i < 26; ++ i)
                suc[i] = NULL;
        }
    };

    state *rt, *lst;

    inline void insert(int c)
    {
        state *u = new state;
        u->len = lst->len + 1;
        for(; lst != NULL && lst->suc[c] == NULL; lst->suc[c] = u, lst = lst->pre);
        if(lst == NULL)
            u->pre = rt;
        else
        {
            state *p = lst->suc[c];
            if(p->len == lst->len + 1)
                u->pre = p;
            else
            {
                state *q = new state;
                *q = *p;
                q->len = lst->len + 1;
                p->pre = u->pre = q;
                for(; lst != NULL && lst->suc[c] == p; lst->suc[c] = q, lst = lst->pre);
            }
        }
        lst = u;
    }

    inline void build(char *str, int len)
    {
        lst = rt = new state;
        rt->len = 0, rt->pre = NULL;
        for(int i = 0; i < len; ++ i)
            insert(str[i] - 'a');
    }

    void getSize(state *u)
    {
        u->tg = 1;
        if(u->pre != NULL)
            ++ u->pre->sz;
        for(int i = 0; i < 26; ++ i)
            if(u->suc[i] != NULL && ! u->suc[i]->tg)
                getSize(u->suc[i]);
    }

    void DFS(state *u)
    {
        u->tg = 2;
        if(u->sz == 1)
        {
            A.modify(u->len - u->pre->len - 1, u->len - 1, u->pre->len + 1);
            if(u->len - u->pre->len - 1 > 0)
                B.modify(0, u->len - u->pre->len - 2, u->len - 1);
        }
        for(int i = 0; i < 26; ++ i)
            if(u->suc[i] != NULL && u->suc[i]->tg == 1)
                DFS(u->suc[i]);
    }

    inline void work()
    {
        getSize(rt);
        DFS(rt);
    }
}SAM;

int main()
{
    #ifndef ONLINE_JUDGE
    freopen("BZOJ1396.in", "r", stdin);
    #endif
    static char str[LEN];
    scanf("%s", str);
    int len = strlen(str);
    SAM.build(str, len);
    SAM.work();
    for(int i = 0; i < len; ++ i)
    {
        int resA = A.query(i), resB = B.query(i);
        printf("%d\n", std::min(resA, resB - i + 1));
    }
}

转载于:https://www.cnblogs.com/ZeonfaiHo/p/7125516.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值