POJ1509 Glass Beads——SAM(后缀自动机)

题面

  http://poj.org/problem?id=1509

解析

  题意就是求字符串的最小表示, 当然可以用O(n)的算法快速求出,码量也不大,但最近讲了SAM,要求我们用SAM做这道题, 那就用SAM做一波

  预处理还是一样,先要复制一遍原字符串,然后建立一个后缀自动机,从根节点开始,用贪心的思路优先选取最小的节点,然后向后跳,跳n步后就是答案, 这里一定可以跳n步,因为如果一开始是在后面区间内开始跳的话,假设这个点是n+p, 那在p点一定可以跳n步, 并且同时保证了字典序不变

  还有一个问题, 知道了序列,如何求出答案?当然不能O(n2)暴力求出, 这里有这个字符串的性质,如果我们的终止节点为q, 假如q点代表的right集合内只有一个点,那么q的len就是这个点的前缀长度,答案就是len-n+1; 如果q点代表的right集合内有不止1个点,容易知道这几个点的right一定在右半区间,并且以这个集合内的几个点为右端点的长度为n的字符串相同, 那么这个字符串存在循环节,这几个端点的前n个字符串相同,由于循环节的存在,最靠左的端点的前缀一定是其他端点的前缀的后缀,那么q点的len就是最靠左的端点的前缀的长度, 答案就是len-n+1。

综上, 答案就是len[q] - n + 1

  代码:

#include<cstdio>
#include<cstring>
using namespace std;
typedef long long ll;
const int maxn = 10005 ;

int T, n, las, cnt;
char s[maxn];

struct point{
    int ch[26], len, fa;
}d[maxn<<2];

void Add(int x)
{
    int p = las, np = las = ++cnt;
    d[np].len = d[p].len + 1;
    for( ; p && !d[p].ch[x] ; p = d[p].fa)    d[p].ch[x] = np;
    if(!p)
        d[np].fa = 1;
    else
    {
        int q = d[p].ch[x];
        if(d[q].len == d[p].len + 1)
            d[np].fa = q;
        else
        {
            int nq = ++cnt;
            d[nq] = d[q];
            d[nq].len = d[p].len + 1;
            d[q].fa = d[np].fa = nq;
            for( ; p && d[p].ch[x] == q; p = d[p].fa)
                d[p].ch[x] = nq;
        }
    }
}

void work()
{
    int now = 1;
    for(int i = 1 ; i <= n; ++i)
        for(int j = 0; j < 26; ++j)
            if(d[now].ch[j])
            {
                now = d[now].ch[j];
                break;
            }
    printf("%d\n", d[now].len - n + 1);
}

int main()
{
    scanf("%d", &T);
    while(T--)
    {
        las = cnt = 1;
        memset(d,0,sizeof(d));
        scanf("%s", s+1);
        n = strlen(s+1);
        for(int i = 1; i <= n; ++i)
            Add(s[i]-'a');
        for(int i = 1; i <= n; ++i)
            Add(s[i]-'a');
        work();
    }
    return 0;
}
View Code

 

转载于:https://www.cnblogs.com/Joker-Yza/p/11197723.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值