HDU 5769后缀数组

来源:互联网 发布:lte中d2怎么优化 编辑:程序博客网 时间:2024/05/01 01:46

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);    }}


0 0