HDU 5769 (后缀数组)

本文介绍了一种使用后缀数组求解包含特定字符X的所有不同子串的方法。通过倍增算法构建后缀数组,并结合高度数组进行优化,实现了高效求解。

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

题目链接:点击这里

题意:求出一个串中所有不同的含有字母X的子串。

后缀数组的经典运用就有求出一个串的所有子串,方法是从小到大考虑所有的 sai ( sai 表示字典序第i小的后缀的下标),因为 height 数组的含义是 sai sai1 两个后缀的相同前缀长度,而 sai 后缀有 nsai 个前缀,减去重复的 heighti 个前缀就是不重复的子串,然后 一下。 在这里就是要考虑含有X这个限制条件。 sai 后缀不再是有 nsai 个前缀,而是要找到它之后的第一个X字母才能确定前缀个数,然后结合 height 数组瞎搞搞就好了。

#include <iostream>
#include <cstdio>
#include <cstring>
#include <queue>
#include <cmath>
#include <string>
#include <vector>
#include <algorithm>
#include <map>
#include <set>
#define maxn 111111
using namespace std;
//以下为倍增算法求后缀数组
int wa[maxn],wb[maxn],wv[maxn],Ws[maxn];
int cmp(int *r,int a,int b,int l)
{return r[a]==r[b]&&r[a+l]==r[b+l];}
void da(const char *r,int *sa,int n,int m){
    int i,j,p,*x=wa,*y=wb,*t; 
    for(i=0;i<m;i++) Ws[i]=0; 
    for(i=0;i<n;i++) Ws[x[i]=r[i]]++; 
    for(i=1;i<m;i++) Ws[i]+=Ws[i-1]; 
    for(i=n-1;i>=0;i--) sa[--Ws[x[i]]]=i; 
    for(j=1,p=1;p<n;j*=2,m=p){ 
        for(p=0,i=n-j;i<n;i++) y[p++]=i; 
        for(i=0;i<n;i++) if(sa[i]>=j) y[p++]=sa[i]-j; 
        for(i=0;i<n;i++) wv[i]=x[y[i]]; 
        for(i=0;i<m;i++) Ws[i]=0; 
        for(i=0;i<n;i++) Ws[wv[i]]++; 
        for(i=1;i<m;i++) Ws[i]+=Ws[i-1]; 
        for(i=n-1;i>=0;i--) sa[--Ws[wv[i]]]=y[i]; 
        for(t=x,x=y,y=t,p=1,x[sa[0]]=0,i=1;i<n;i++) 
            x[sa[i]]=cmp(y,sa[i-1],sa[i],j)?p-1:p++; 
    } 
    return; 
}
int sa[maxn],Rank[maxn],height[maxn];
//求height数组
void calheight(const char *r,int *sa,int n){
    int i,j,k=0;
    for(i=1;i<=n;i++) Rank[sa[i]]=i;
    for(i=0;i<n;height[Rank[i++]]=k)
        for(k?k--:0,j=sa[Rank[i]-1];r[i+k]==r[j+k];k++);
    return;
}
char str[maxn];

int pos[maxn];
int cnt;
char ch[2];
long long slove(int n){
    long long sum=0;
    for (int i = 1; i <= n; i++) {
        int id = lower_bound (pos, pos+cnt, sa[i])-pos; 
        id = (id >= cnt ? n : pos[id]);
        int len = id-sa[i];
        if (height[i] >= len)
            sum += n-id-(height[i]-len);
        else 
            sum += n-id;
    }
    return sum;
}

int main(){
    int t, kase = 0;
    scanf("%d",&t);
    while(t--){
        scanf("%s%s", ch, str);
        da(str,sa,strlen(str)+1,130);
        cnt = 0;
        calheight(str,sa,strlen(str));
        int n = strlen (str);
        for (int i = 0; i < n; i++) if (str[i] == ch[0]) {
            pos[cnt++] = i;
        }
        printf("Case #%d: %lld\n", ++kase, slove(n));
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值