zoukankan      html  css  js  c++  java
  • HDU 6194 string string string 2017沈阳网络赛 后缀数组

    题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=6194

    题意:告诉你一个字符串和k , 求这个字符串中有多少不同的子串恰好出现了k 次。

    解法:后缀数组。我们先考虑至少出现k 次的子串, 所以我们枚举排好序的后缀i (sa[i]) 。然后k段k 段的枚举。假设当前枚举的是 sa[i]~sa[i + k -1],那么假设这一段的最长公共前缀  是L 的话。那么就有L 个不同的子串至少出现了k次。我们要减去至少出现k + 1次的 , 但还要和这个k 段的lcp 有关系, 因此肯定就是 这一段 向上找一个后缀 或者向下找一个后缀。即  sa[i-1] ~ sa[i + k - 1]  和 sa[i] ~ sa[i + k] 求两次lcp 减去即可。但是会减多了。减多的显然是sa[i-1] ~ sa[i + k] 的lcp。 加上即可。但是这是没法处理k=1的情况的,k=1的时候我们直接特判掉,k=1的时候不同字符串个数就是n-sa[i]。

    #include <bits/stdc++.h>
    using namespace std;
    typedef long long LL;
    const int maxn = 100010;
    int sa[maxn];//SA数组,表示将S的n个后缀从小到大排序后把排好序的
                 //的后缀的开头位置顺次放入SA中
    int t1[maxn],t2[maxn],c[maxn];//求SA数组需要的中间变量,不需要赋值
    int Rank[maxn],height[maxn];
    //待排序的字符串放在s数组中,从s[0]到s[n-1],长度为n,且最大值小于m,
    //除s[n-1]外的所有s[i]都大于0,r[n-1]=0
    //函数结束以后结果放在sa数组中
    void build_sa(int s[],int n,int m)
    {
        int i,j,p,*x=t1,*y=t2;
        //第一轮基数排序,如果s的最大值很大,可改为快速排序
        for(i=0;i<m;i++)c[i]=0;
        for(i=0;i<n;i++)c[x[i]=s[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;
            //直接利用sa数组排序第二关键字
            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;
            //这样数组y保存的就是按照第二关键字排序的结果
            //基数排序第一关键字
            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];
            //根据sa和x数组计算新的x数组
            swap(x,y);
            p=1;x[sa[0]]=0;
            for(i=1;i<n;i++)
                x[sa[i]]=y[sa[i-1]]==y[sa[i]] && y[sa[i-1]+j]==y[sa[i]+j]?p-1:p++;
            if(p>=n)break;
            m=p;//下次基数排序的最大值
        }
    }
    void getHeight(int s[],int n)
    {
        int i,j,k=0;
        for(i=0;i<=n;i++)Rank[sa[i]]=i;
        for(i=0;i<n;i++)
        {
            if(k)k--;
            j=sa[Rank[i]-1];
            while(s[i+k]==s[j+k])k++;
            height[Rank[i]]=k;
        }
    }
    int n,k,dp[maxn][30];
    int r[maxn];
    char s[maxn];
    void Lcp_init(){
        for(int i=1; i<=n+1; i++) dp[i][0] = height[i];
        for(int j=1; (1<<j)<=n+1; j++){
            for(int i=0; i+(1<<j)<n+2; i++){
                dp[i][j] = min(dp[i][j-1], dp[i+(1<<(j-1))][j-1]);
            }
        }
    }
    int lcp(int l, int r){
        if(l == r) return n - sa[r];
        if(l>r) swap(l, r);
        ++l;
        int k=0,len=r-l+1;
        while((1<<(k+1))<=len) ++k;
        return min(dp[l][k], dp[r-(1<<k)+1][k]);
    }
    
    int main()
    {
        int T;
        scanf("%d", &T);
        while(T--){
            scanf("%d", &k);
            scanf("%s", s);
            n = strlen(s);
            for(int i=0; i<n; i++) r[i]=s[i]-'a'+1;
            r[n]=0;
            build_sa(r, n+1, 128);
            getHeight(r, n);
            Lcp_init();
            LL ans = 0;
            for(int i=1; i+k-1<=n; i++){
                ans += lcp(i, i+k-1);
                if(i-1 > 0) ans -= lcp(i-1,i+k-1);
                if(i+k <= n) ans -= lcp(i, i+k);
                if(i-1 >0 && i+k<=n) ans += lcp(i-1, i+k);
            }
            printf("%lld
    ", ans);
        }
        return 0;
    }
    
  • 相关阅读:
    www.insidesql.org
    kevinekline----------------- SQLSERVER MVP
    Sys.dm_os_wait_stats Sys.dm_performance_counters
    如何使用 DBCC MEMORYSTATUS 命令来监视 SQL Server 2005 中的内存使用情况
    VITAM POST MORTEM – ANALYZING DEADLOCKED SCHEDULERS MINI DUMP FROM SQL SERVER
    Cargo, Rust’s Package Manager
    建筑识图入门(初学者 入门)
    Tracing SQL Queries in Real Time for MySQL Databases using WinDbg and Basic Assembler Knowledge
    Microsoft SQL Server R Services
    The Rambling DBA: Jonathan Kehayias
  • 原文地址:https://www.cnblogs.com/spfa/p/7504916.html
Copyright © 2011-2022 走看看