[BZOJ 1396] 识别子串

本文分享了一个使用后缀数组解决字符串匹配问题的实际案例。通过分析题目要求,采用后缀数组结合单调队列的方法,有效地解决了问题,并给出了完整的代码实现。

http://www.lydsy.com/JudgeOnline/problem.php?id=1396

Solution:
我得了“能用后缀数组就一定不用后缀自动机综合症”….感觉用height、rank、sa 处理字符串真的好优雅….
虽然其他题上后缀数组常数比较大…不过这个后缀数组水了一发似乎就Rank1 了…开心
题解就是求出后缀数组以后维护一个单调队列来更新答案啦。。。

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

#define rep(i, x, y) for (int i = (x), _ = (y); i <= _; ++i)
#define down(i, x, y) for (int i = (x), _ = (y); i >= _; --i)
#define x first
#define y second
#define LX_JUDGE

using namespace std;
typedef long long LL;

template<typename T> inline void up_max(T & x, T y) { x < y ? x = y : 0; }
template<typename T> inline void up_min(T & x, T y) { x > y ? x = y : 0; }

template<typename T>
inline void read(T & x)
{
    char c;
    while ((c = getchar()) < '0' || c > '9') ;
    for (x = c - '0'; (c = getchar()) >= '0' && c <= '9'; x = x * 10 + c - '0') ;
}

const int inf = 0x3f3f3f3f ;
const int N = 1e5 + 10;

namespace Suffix_Array
{
    int Sa[N], Rank[N], height[N];

    void Suffix_Da(char * r, int n, int m)
    {
        static int ws[N];
        int i, j, p, *x = Rank, *y = height;

        memset(ws, 0, sizeof(int) * m);
        for (i = 0; i < n; ++i)
            ++ws[x[i] = r[i]];
        for (i = 1; i < m; ++i)
            ws[i] += ws[i - 1];
        for (i = n - 1; ~i; --i)
            Sa[--ws[x[i]]] = i;
        for (j = p = 1; p < n; j <<= 1, m = p)
        {
            for (p = 0, i = n - j; i < n; ++i)
                y[p++] = i;
            for (i = 0; i < n; ++i) if (Sa[i] >= j)
                y[p++] = Sa[i] - j;
            memset(ws, 0, sizeof(int) * m);
            for (i = 0; i < n; ++i)
                ++ws[x[i]];
            for (i = 1; i < m; ++i)
                ws[i] += ws[i - 1];
            for (i = n - 1; ~i; --i)
                Sa[--ws[x[y[i]]]] = y[i];
            swap(x, y);
            x[Sa[0]] = 0, p = 1;
            for (i = 1; i < n; ++i)
                x[Sa[i]] = (y[Sa[i - 1]] == y[Sa[i]] && y[Sa[i - 1] + j] == y[Sa[i] + j]) ? p - 1 : p++;
        }
        for (i = 0; i < n; ++i)
            Rank[Sa[i]] = i;
        for (p = 0, i = 0; i < n - 1; ++i)
        {
            p ? --p : 0;
            j = Sa[Rank[i] - 1];
            while (r[i + p] == r[j + p])
                ++p;
            height[Rank[i]] = p;
        }
        height[n] = height[0] = height[1] = 0;
    }
}

namespace My_Worker
{
    typedef pair<int , int> Data;

    Data que[N];
    int left = 1, right = 0;

    void insert(Data u)
    {
        while (left <= right && u.y - u.x <= que[right].y - que[right].x)
            --right;
        que[++right] = u;
    }
    int query(int x)
    {
    #define calc(o) (max((o).y, x) - (o).x + 1)
        while (left < right && calc(que[left + 1]) <= calc(que[left]))
            ++left;
        return calc(que[left]);
    #undef calc
    }
}

char str[N];

int main()
{
#ifdef LX_JUDGE
    freopen("in.txt", "r", stdin);
#endif
    using namespace Suffix_Array;
    using namespace My_Worker;

    scanf("%s", str);
    int n = strlen(str);
    str[n++] = 0;

    Suffix_Da(str, n, 128);

    rep (i, 0, n - 2)
    {
        int tmp = i + max(height[Rank[i]], height[Rank[i] + 1]);
        if (tmp < n - 1) 
            insert(Data(i, tmp));
        printf("%d\n", query(i));
    }

    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值