POJ 1509 后缀自动机+KMP

题意:给定n个字符串,求每个字符串最小表示法是从被给定的串的第几位开始

求最小表示法可以直接在后缀自动机中跑n个字符,后缀自动机是根据原串复制成2倍长度建立的,所以在自动机中按照最小的边跑n个字符得到的就是被给定的字符串的最小表示法,然后再用KMP与2n长度的字符串匹配即可。【蒟蒻的无脑解法】

#include <cstdio>
#include <algorithm>
#include <cstring>
using namespace std;

int T, n, k[10005];
char s[20005], t[10005];

struct SAM
{
    #define MX 50005
    int tot, last, root, l[MX], f[MX], go[MX][30], flag[MX];
    void initialize ()
    {
        tot = last = root = 1;
        memset (f, 0, sizeof f);
        memset (l, 0, sizeof l);
        memset (go, 0, sizeof go);
        memset (flag, 0x3f, sizeof flag);
    }
    void add (char c)
    {
        int x = c-'a'+1;
        int p = last, np = ++tot; last = np;
        l[np] = l[p] + 1;
        for(;p&&!go[p][x];p=f[p]) 
            go[p][x] = np, flag[p] = min(flag[p],x);
        if (!p) f[np] = root;
        else
        {
            int q = go[p][x];
            if (l[q] == l[p]+1) f[np] = q;
            else
            {
                int nq = ++tot; l[nq] = l[p] + 1;
                memcpy(go[nq], go[q], sizeof go[q]);
                f[nq] = f[q];
                f[np] = f[q] = nq;
                flag[nq] = flag[q];
                for(;go[p][x]==q;p=f[p]) 
                    go[p][x] = nq, flag[p] = min(flag[p],x);
            }
        }
    }

    void get_t ()
    {
        int p = root;
        for (int i = 1; i <= n; i++)
        {
            t[i] = flag[p]+'a'-1;
            p = go[p][flag[p]];
        }
    }
}Solve;

int KMP ()
{
    int m = n; n *= 2;
    k[1] = k[2] = 1;
    for (int i = 2, j = 1; i <= m; j=k[++i])
    {
        while (j > 1 && t[j] != t[i]) j = k[j];
        k[i+1] = (t[j]==t[i])*j+1;
    }
    for (int i = 1, j = 1; i <= n; i++)
    {
        while (j > 1 && s[i] != t[j]) j = k[j];
        if (s[i] == t[j]) j++;
        if (j > m) return i-m-n+1>0?i-m-n+1:i-m+1;
    }
}

int main ()
{
    scanf ("%d", &T);
    while (T--)
    {
        memset (s, 0, sizeof s);
        memset (t, 0, sizeof t);
        scanf ("%s", s+1);
        n = strlen(s+1);
        Solve.initialize();
        for (int i = 1; i <= n; i++) s[i+n] = s[i];
        for (int i = 1; i <= n*2; i++) Solve.add(s[i]);
        Solve.get_t();
        printf ("%d\n", KMP());
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值