zoukankan      html  css  js  c++  java
  • 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
    ",cas++,ans);
        }
    }



  • 相关阅读:
    第十二周总结
    第十一周课程总结
    第十周第十周课程总结
    第九周课程总结&实验报告(七)
    第八周课程总结&实验报告(六)
    第七周课程总结&实验报告(五)
    第六周&java实验报告四
    第五周课程总结&试验报告(三)
    学期总结
    十四周总结
  • 原文地址:https://www.cnblogs.com/zhangxianlong/p/10672546.html
Copyright © 2011-2022 走看看