CF 235C Cyclical Quest

本文深入探讨了后缀自动机(SAM)的实现细节,包括初始化、插入字符及求解特定长度子串出现次数等问题,并附带完整代码示例。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

后缀自动机

几天不碰SAM,已经对SAM一无所知了QAQ

求x的同构,等价于把x复制一份贴在后面求xx的长度大于len(x)的子串出现次数,注意要判重。

一时智障,把当前SAM匹配的节点的len直接当成真实匹配的len了。。。实际上需要记一个curlen表示当前匹配了多长才科学。

#include<cstdio>
#include<cstring>
#define N 1000005
#define A 28
using namespace std; 
namespace runzhe2000
{
    char s[N], x[N*2];
    int n, L, timer, sum[N];
    struct SAM
    {
        SAM *next[A], *fail;
        int len, right, vis;
    }mem[N*3], *tot, *null, *last, *root, *tmp[N*3];
    SAM* newSAM()
    {
        SAM *p = ++tot; *p = *null;
        return p; 
    }
    void init()
    {
        null = tot = mem;
        for(int i = 0; i < A; i++) null->next[i] = null;
        null->fail = null; null->len = null->right = null->vis = 0;
        last = root = newSAM();
    }
    void ins(int v)
    {
        SAM *p = last, *np = newSAM(); np->len = last->len + 1; np->right = 1; last = np; 
        for(; p -> next[v] == null && p != null; p = p->fail) p->next[v] = np;
        if(p == null) np->fail = root;
        else
        {
            SAM *q = p->next[v];
            if(p->len + 1 == q->len) np->fail = q;
            else
            {
                SAM *nq = newSAM(); *nq = *q; nq->len = p->len + 1; nq->right = 0; 
                q->fail = np->fail = nq;
                for(; p->next[v] == q && p != null; p = p->fail) p->next[v] = nq;
            }
        }
    }
    int solve(int len)
    {
        SAM *p = root; ++timer; int ans = 0, curlen = 0;
        for(int i = 1; i <= len; i++)
        {
            int v = x[i] - 'a';
            for(; p->next[v] == null && p != null; p = p->fail) curlen = p->fail->len;
            if(p->next[v] == null) p = root, curlen = 0;
            else p = p -> next[v], curlen++;
            for(; p->fail->len >= len/2; p = p->fail) curlen = p->fail->len;
            if(curlen >= len/2 && p->vis != timer) p->vis = timer, ans += p->right;
        }
        return ans;
    }
    void main()
    {
        scanf("%s%d",s+1,&n);
        L = strlen(s+1); init();
        for(int i = 1; i <= L; i++)ins(s[i] - 'a');

        for(SAM *p = tot; p != mem; p--) sum[p->len]++;
        for(int i = 1; i < N; i++) sum[i] += sum[i-1];
        for(SAM *p = tot; p != mem; p--) tmp[sum[p->len]--] = p;
        for(int i = tot-mem; i; i--) tmp[i]->fail->right += tmp[i]->right;
        for(int _n = 1; _n <= n; _n++)
        {
            scanf("%s",x+1);
            int len = strlen(x+1);
            for(int i = 1; i <= len; i++)
                x[i+len] = x[i];
            len *= 2;
            int ans = solve(len);
            printf("%d\n",ans);
        }
    }
}
int main()
{
    runzhe2000::main();
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值