• hdu 6194 string string string


    https://acm.hdu.edu.cn/showproblem.php?pid=6194

    题意:

    给出一个串,询问他有多少个子串恰好出现k次

    用后缀数组的height数组做

    我们枚举按照rank排序的长为k的后缀子区间[l,r]

    设这段区间的最长公共前缀位lcp

    那么可以得出结论:以sa[l]开始的,长度为1、2、……lcp的子串都至少出现了k次

    但是题目要求恰好k次

    我们记看第l个和第l-1个后缀的最长公共前缀为p1

    第r个和第r+1个后缀的最长公共前缀为p2

    那么可以得出结论

    以sa[l]开始的,长度为1、2、……max(p1,p2) 的子串都出现了超过k次

    所以lcp-max(p1,p2)可以得到以sa[l]开始的恰好出现k次的子串个数

    注意几点:

    1、l-1, r+1 如果越界,用0代替

    2、我的写法因为height是相邻的两个串的lcp,所以k=1要 特判

    3、后缀数组求height数组,在这里比较要用原串,不能用ASCII码,因为字符串结束符ASCII码是0

    #include<bits/stdc++.h>
    
    using namespace std;
    
    #define N 100009
    
    char ch[N];
    int n,k,a[N],v[N],p,q,sa[2][N],rk[2][N],h[N];
    
    multiset<int>s;
    multiset<int>::iterator it;
    
    void mul(int *sa,int *rk,int *SA,int *RK)
    {
        for(int i=1;i<=n;i++) v[rk[sa[i]]]=i;
        for(int i=n;i;i--)
            if(sa[i]>k) 
                SA[v[rk[sa[i]-k]]--]=sa[i]-k;
        for(int i=n-k+1;i<=n;i++) 
            SA[v[rk[i]]--]=i;
        for(int i=1;i<=n;i++) 
            RK[SA[i]]=RK[SA[i-1]]+(rk[SA[i]]!=rk[SA[i-1]]||rk[SA[i]+k]!=rk[SA[i-1]+k]);
    }
    
    void presa()
    {
        p=0;
        q=1;
        for(int i=1;i<=127;++i) v[i]=0;
        for(int i=1;i<=n;++i) rk[0][i]=rk[1][i]=0;
        for(int i=1;i<=n;i++) v[a[i]]++;
        for(int i=1;i<=127;i++) v[i]+=v[i-1];
        for(int i=1;i<=n;i++) 
            sa[p][v[a[i]]--]=i;
        for(int i=1;i<=n;i++) 
            rk[p][sa[p][i]]=rk[p][sa[p][i-1]]+(a[sa[p][i-1]]!=a[sa[p][i]]);
        for(k=1;k<n;k<<=1,swap(p,q))
            mul(sa[p],rk[p],sa[q],rk[q]);
        for(int i=1,k=0;i<=n;i++)
        {
            int j=sa[p][rk[p][i]-1];
            while(ch[i+k]==ch[j+k]) k++;
            h[rk[p][i]]=k;if(k) k--;
        }
    }
    
    void solve(int m)
    {
        h[n+1]=0;
        long long ans=0;
        if(m==1)
        {
            for(int i=1;i<=n;++i) ans+=(n-sa[p][i]+1)-max(h[i],h[i+1]);
            printf("%lld
    ",ans);
            return; 
        }
        s.clear();
        for(int i=1;i<m-1;++i) s.insert(h[i+1]);
        int lcp,u,d,last=0;
        for(int i=m;i<=n;++i)
        {    
            s.insert(h[i]);
            it=s.begin();
            lcp=*it;
            u=last;
            d=h[i+1];
            ans+=max(0,lcp-max(u,d));
            last=h[i-m+2];
            it=s.find(last);
            s.erase(it);
        }
        printf("%lld
    ",ans); 
    }
    
    int main()
    {
       int T,m;
       scanf("%d",&T);
       while(T--)
       {
               scanf("%d",&m);
               scanf("%s",ch+1);
               n=strlen(ch+1);
               for(int i=1;i<=n;++i) a[i]=ch[i];
               presa();
               solve(m);
       }
    }
    作者:xxy
    本文版权归作者和博客园共有,转载请用链接,请勿原文转载,Thanks♪(・ω・)ノ。
  • 相关阅读:
    使用iOS网络请求
    Invalid RNPermission 'ios.permission.xxx'. should be one of: ( )
    React Native 报错 Error: spawn EACCES 权限
    React Native 适配Android物理返回键,实现连续两次点击退出
    图解:平衡二叉树,AVL树
    NOIP 骗分技巧
    P1004 方格取数
    5. 最长回文子串
    全链路压测自动化实践
    深度学习在美团配送ETA预估中的探索与实践
  • 原文地址:https://www.cnblogs.com/TheRoadToTheGold/p/15256951.html
Copyright © 2020-2023  润新知