hdu 5769 后缀数组

分析:

首先知道求不同子串个数的做法,就是,每个后缀减去它的高度数组。这相当于一种划分,然后减去交叉部分。
这里多了一个含有字符x,求每个字符后面第一个x出现的位置,再去减去交叉部分和不含x部分的较大值即可。

#include <cstdio>
#include <cstring>
#include <string>
#include <iostream>
#include <algorithm>
using namespace std;
#define pr(x) cout << #x << ": " << x << "  " 
#define pl(x) cout << #x << ": " << x << endl;

//后缀数组模板,时间复杂度nlogn,空间复杂度n
struct Suffix
{
    static const int maxn = int(1e5) + 13;

    int r[maxn];
    //后缀数组,名词数组,高度数组(表示和它前一名后缀的最长前缀长度)。
    int sa[maxn],rnk[maxn],height[maxn];
    int t[maxn],t2[maxn],c[maxn],n,m;

    char str[maxn];

    //把string转化为r[i]
    void init_string(string &s) {
        n = s.size();
        for(int i=0;i<n;i++) r[i]=(int)s[i];
        m = 128;
    }

    void init_char(char *s) {
        n = strlen(s);
        for(int i=0; i<n; i++) r[i]=(int)s[i];
        m = 128;
    }

    int cmp(int *r,int a,int b,int l) { return r[a]==r[b]&&r[a+l]==r[b+l]; }

    //处理得到后缀数组
    void build() {
        int i,k,p,*x=t,*y=t2;
        r[n++]=0;
        for (i=0; i<m; i++) c[i]=0;
        for (i=0; i<n; i++) c[x[i]=r[i]]++;
        for (i=1; i<m; i++) c[i]+=c[i-1];
        for (i=n-1; i>=0; i--) sa[--c[x[i]]]=i;
        for (k=1,p=1; k<n; k*=2,m=p) {
            for (p=0,i=n-k; i<n; i++) y[p++]=i;
            for (i=0; i<n; i++) if (sa[i]>=k) y[p++]=sa[i]-k;
            for (i=0; i<m; i++) c[i]=0;
            for (i=0; i<n; i++) c[x[y[i]]]++;
            for (i=1; i<m; i++) c[i]+=c[i-1];
            for (i=n-1; i>=0; i--) sa[--c[x[y[i]]]]=y[i];
            swap(x,y);
            p=1;
            x[sa[0]]=0;
            for (i=1; i<n; i++) x[sa[i]]=cmp(y,sa[i-1],sa[i],k)?p-1:p++;
        }
        n--;
    }

    //处理得到高度数组和名次数组
    void lcp() {
        int i,j,k=0;
        for (i=1; i<=n; i++) rnk[sa[i]]=i;
        for (i=0; i<n; i++) {
            if (k) k--;
            j=sa[rnk[i]-1];
            while (r[i+k]==r[j+k]) k++;
            height[rnk[i]]=k;
        }
    }
}suffix;

struct jibancanyang
{
    int T, dp[suffix.maxn];
    char x[1], s[suffix.maxn];

    void fun() {
        scanf("%d", &T);
        for (int cas = 1; cas <= T; ++cas) {
            scanf("%s%s", x, s);
            int slen = strlen(s);
            dp[slen] = slen;
            for (int i = slen - 1; i >= 0; --i) {
                if (s[i] == x[0]) dp[i] = i;
                else dp[i] = dp[i + 1];
            }

            suffix.init_char(s);
            suffix.build();
            suffix.lcp();

            long long ans = 0;
            for (int i = 1; i <= slen; ++i) {
                ans += slen - suffix.sa[i] - max(suffix.height[i],
                        dp[suffix.sa[i]] - suffix.sa[i]);
            }
            printf("Case #%d: %lld\n", cas, ans);
        }
    }


}ac;

int main()
{
#ifdef LOCAL
    freopen("in.txt", "r", stdin);
    //freopen("out.txt", "w", stdout);
#endif
    ac.fun();
    return 0;
}


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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值