2019年我能变强组队训练赛热身赛 Problem H String and Times(后缀自动机)

链接:http://icpc.upc.edu.cn/running.php?cid=1828

题意:多组样例,每行给出一个字符串S,两个整数A,B。问一个字符串中出现次数在[A,B]范围内的子串的种类数。

思路:后缀自动机模板题。但是,题目说范围是2e6,数组开这么大会报运行错误,看到1e5就过了。oj问题。。。。

#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<vector>
#define ll long long
#define llu unsigned ll
using namespace std;
const int maxn=1e5+10;
char str[maxn];
 
int x[maxn<<1],y[maxn<<1];
struct Sam
{
    int last,cnt;
    int nt[maxn<<1][26],len[maxn<<1];
    int fa[maxn<<1],sum[maxn<<1];
 
    void init(int n)
    {
        last=1;
        cnt=1;
        fa[1]=0;
        len[1]=0;
 
        for(int i=0;i<=n;i++)
        {
            fa[i]=0;
            len[i]=0;
            sum[i]=0;
            memset(nt[i],0,sizeof(nt[i]));
        }
    }
 
    void _insert(int c)
    {
        int nowp=++cnt,p=last;
        len[nowp]=len[last]+1;
 
        while(p&&!nt[p][c]) nt[p][c]=nowp,p=fa[p];
 
        if(!p) fa[nowp]=1;
        else
        {
            int q=nt[p][c];
            if(len[q]==len[p]+1) fa[nowp]=q;
            else
            {
                int nowq=++cnt;
                len[nowq]=len[p]+1;
                memcpy(nt[nowq],nt[q],sizeof(nt[q]));
 
                fa[nowq]=fa[q];
                fa[nowp]=fa[q]=nowq;
 
                while(p&&nt[p][c]==q) nt[p][c]=nowq,p=fa[p];
            }
        }
        last=nowp,sum[last]=1;
        return ;
    }
 
    void _count(void)
    {
        memset(x,0,sizeof(x));
        for(int i=1;i<=cnt;i++) x[len[i]]++;
        for(int i=1;i<=cnt;i++) x[i]+=x[i-1];
        for(int i=1;i<=cnt;i++) y[x[len[i]]--]=i;
        for(int i=cnt;i>=1;i--) sum[fa[y[i]]]+=sum[y[i]];
        return ;
    }
 
    void creat(void)
    {
        int len=strlen(str);
 
        init((len+5)*2);
        for(int i=0;i<len;i++)
            _insert(str[i]-'A');
        _count();
 
        return ;
    }
}sam;
 
int main(void)
{
 
    int k1,k2;
    while(~scanf("%s%d%d",str,&k1,&k2))
    {
        sam.creat();
        ll ans=0;
        for(int i=1;i<=sam.cnt;i++)
        {
            if(sam.sum[i]>=k1&&sam.sum[i]<=k2)
            {
                ans+=(sam.len[i]-sam.len[sam.fa[i]]);
            }
        }
        printf("%lld\n",ans);
    }
    return 0;
}

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值