HDU 5769后缀数组

http://acm.hdu.edu.cn/showproblem.php?pid=5769

 

题意:给你一个串,问你含某个特定的字符的不同子串有多少种。。

 

思路:使用后缀数组。。很久之前看过做过几个模板水题。。以为自己做不到这么高深的题目。。结果这次就卡了,想hash莽一下发现很不科学,就放弃了。。看来后缀数组也已经是标配了。。赶紧再补充补充自己。。

首先退一步讲,后缀数组如何求不同子串个数。。。其实就是利用排序的性质来解决。。假设我们得到了sa和height。。那么我们来枚举每一个后缀串对于答案的贡献。。显然不考虑重复肯定是len-sa[ i ]。。也就是总长度减去现在的位置,这个很好理解。。那么如何去除重复呢?利用排序的性质和height的意义。。也就是排序好的相邻的最长公共前缀。。那么我们就可以知道有多少是重复的,这样就是len-sa[ i ]-height[ i ]。。到这里问题基本就解决了。

 

回到这个题目,要包含某个特定的字符,其实如果知道每个后缀串要做出贡献至少要多长就可以。。如果知道了,套用上面的就好啦~

 

PS。后缀数组的原理还是不是很懂。。不过重在应用。。慢慢学习。。

 

代码:

 

 

#include <bits/stdc++.h>
using namespace std;


const int MAXN=200010;
int t1[MAXN],t2[MAXN],c[MAXN];//求SA数组需要的中间变量,不需要赋值
bool cmp(int *r,int a,int b,int l){
    return r[a] == r[b] && r[a+l] == r[b+l];
}
void da(int str[],int sa[],int ran[],int height[],int n,int m){
    n++;
    int i, j, p, *x = t1, *y = t2;
    for(i = 0; i < m; i++)c[i] = 0;
    for(i = 0; i < n; i++)c[x[i] = str[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(j = 1; j <= n; j <<= 1){
        p = 0;
        for(i = n-j; i < n; i++)y[p++] = i;//后面的j个数第二关键字为空的最小
        for(i = 0; i < n; i++)if(sa[i] >= j)y[p++] = sa[i] - j;
        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],j)?p-1:p++;
        if(p >= n)break;
        m = p;//下次基数排序的最大值
    }
    int k = 0;
    n--;
    for(i = 0; i <= n; i++)ran[sa[i]] = i;
    for(i = 0; i < n; i++){
        if(k)k--;
        j = sa[ran[i]-1];
        while(str[i+k] == str[j+k])k++;
        height[ran[i]] = k;
    }
}
int ran[MAXN],height[MAXN];
char str[MAXN];
int r[MAXN];
int sa[MAXN];
int nxt[MAXN];

int main(){
    int t,cas=1;
    scanf("%d",&t);
    while(t--){
        char X[2];
        scanf("%s%s",X,str);
        int len = strlen(str);
        for(int i = 0; i < len; i++)r[i] = str[i];
        r[len] = 0;
        da(r,sa,ran,height,len,128);
        int pr=-1;
        for(int i=len-1;i>=0;i--){
            if(X[0]==str[i]) pr=i;
            nxt[i]=pr;
        }
        long long ans=0;
        for(int i=1;i<=len;i++){
            if(nxt[sa[i]]!=-1)
            ans+=len-max(nxt[sa[i]],height[i]+sa[i]);
        }
        printf("Case #%d: %lld\n",cas++,ans);
    }
}



posted @ 2016-07-31 18:47  zhangxianlong  阅读(85)  评论(0编辑  收藏  举报