2018 焦作网络赛 String and Times 后缀数组+线段树

题目链接:https://nanti.jisuanke.com/t/A2018

题意:出现[L, R] 次的字符串的个数

题解:和这个题几乎一样:https://blog.csdn.net/mmk27_word/article/details/98210376,这样我们就固定左边界,找到至少出现L次的 - 至少出现R+1次的即可

#include<iostream>
#include<cstdio>
#include<cstring>
#include<string>
using namespace std;
typedef long long ll;
const int N=110000;
int t1[N],t2[N],sum[N],rk[N],ht[N],sa[N],str[N],n;
char s[N];
void get_sa(int n,int m)
{
    int *x=t1,*y=t2;
    for(int i=0;i<m;i++) sum[i]=0;
    for(int i=0;i<n;i++) sum[x[i]=str[i]]++;
    for(int i=1;i<m;i++) sum[i]+=sum[i-1];
    for(int i=n-1;i>=0;i--) sa[--sum[x[i]]]=i;
    for(int p,j=1;p<=n;j<<=1)
    {
        p=0;
        for(int i=n-j;i<n;i++) y[p++]=i;
        for(int i=0;i<n;i++) if(sa[i]>=j) y[p++]=sa[i]-j;
        for(int i=0;i<m;i++) sum[i]=0;
        for(int i=0;i<n;i++) sum[x[y[i]]]++;
        for(int i=1;i<m;i++) sum[i]+=sum[i-1];
        for(int i=n-1;i>=0;i--) sa[--sum[x[y[i]]]]=y[i];
        swap(x,y);
        p=1;
        x[sa[0]]=0;
        for(int 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++;
        if(p>=n) break;
        m=p;
    }
    int k=0;n--;
    for(int i=0;i<=n;i++) rk[sa[i]]=i;
    for(int i=0;i<n;i++)
    {
        if(k)k--;else k=0;
        int j=sa[rk[i]-1];
        while(str[i+k]==str[j+k])k++;
        ht[rk[i]]=k; 
    }
}
struct node {
    int l, r;
    int minn;
}tree[N<<2];
void build(int l, int r, int cur) {
    tree[cur].l=l;
    tree[cur].r=r;
    if(l==r) {
        tree[cur].minn=ht[l];
        return;
    }
    int mid=(r+l)>>1;
    build(l, mid, cur << 1);
    build(mid + 1, r, cur << 1 | 1);
    tree[cur].minn = min(tree[cur<<1].minn, tree[cur<<1|1].minn);
}
int query(int pl, int pr, int cur) {
    if(pl <= tree[cur].l && tree[cur].r <= pr) {
        return tree[cur].minn;
    }
    int res = N;
    if(pl <= tree[cur<<1].r) res = min(res, query(pl, pr, cur << 1));
    if(pr >= tree[cur<<1|1].l) res = min(res, query(pl, pr, cur << 1 | 1));
    return res;
}
 
int main()
{
    int T;
    char s[100100];
    int n,k;
    ll ans1 , ans2;
    int l, r;
    int A, B;
    int cnt;
    while(~scanf("%s%d%d",s,&A,&B)) {
        n=strlen(s);
        for(int i=0;i<n;i++) str[i]=s[i];
        str[n]=0;
        get_sa(n+1,256);
        ht[n+1]=0;
        ans1 = ans2 = 0;
        build(1,n,1);
        ht[n + 1] = 0;
        if(A == 1) {
            ans1 = 1LL * n * (n + 1) / 2;
            for(int i = 1; i <= n; i++) {
                ans1 -= ht[i];
            }
        } else {
            for(int i = 1; i <= n - A + 1; i++) {
                l = i + 1, r = i + A - 1;
                cnt = 0;
                if(l <= r) cnt = query(l, r, 1);
                ans1 += max(0, cnt - ht[i]);
            }
        }
        B++;    
        if(B <= n) {
            for(int i = 1; i <= n - B + 1; i++) {
                l = i + 1, r = i + B - 1;
                cnt = 0;
                if(l <= r) cnt = query(l, r, 1);
                ans2 += max(0, cnt - ht[i]);
            }
        }
        printf("%lld\n", ans1 - ans2);
    }
     
     
    return 0;
} 

 

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值