zoukankan      html  css  js  c++  java
  • hdu 6988 / 2021“MINIEYE杯”中国大学生算法设计超级联赛(4)1004 Display Substring(后缀数组+二分)

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

    题意:

    给出一个字符串,每个字母都有一定的价值,子串的价值为各个字母价值总和

    问所有不同的子串中,价值第k小的子串的价值是多少

    首先二分一个价值,检验这个价值是否满足要求

    一开始的二分写的 若小于等于二分值的子串数量<=k就更新答案,是错误的

    应该是 若小于等于二分值的子串数量>=k就更新答案

    因为可能存在多个不同的子串价值相同,不加他们不够k,加上他们之后超过k

    如何求子串价值小于等于某个值的子串数量?

    一个字符串的所有后缀的所有前缀就是所有的子串

    如果没有不同子串的限制:

    枚举后缀的起始位置,利用前缀和,二分可以求出这个后缀的多少个前缀价值<=二分的值

    现在要求只计算不同的子串

    利用后缀数组,按照rank从小到大算

    因为排名相邻的两个串的最长公共前缀是height[i],也就是说排名为i的后缀的前height[i]个前缀都在排名为i-1的后缀里包含了

    只需要对这个后缀二分的时候更改一下二分的下界即可

    #include<bits/stdc++.h>
    
    using namespace std;
    
    #define N 100002
    
    char ch[N];
    int n,k,a[N],v[N],p,q,sa[2][N],rk[2][N],h[N];
    
    long long m;
    int w[30],val[N];
    
    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<=26;++i) v[i]=0;
        for(int i=1;i<=n;i++) v[a[i]]++;
        for(int i=1;i<=26;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(a[i+k]==a[j+k]) k++;
            h[rk[p][i]]=k;if(k) k--;
        }
    }
    
    long long check(int lim)
    {
        int l,r=n,mid,tmp;
        long long sum=0;
        for(int i=1;i<=n;++i)
        {
            l=sa[p][i]+h[i];
            r=n;
            tmp=l-1;
            while(l<=r)
            {
                mid=l+r>>1;
                if(val[mid]-val[sa[p][i]-1]<=lim) 
                {
                    tmp=mid;
                    l=mid+1;
                }
                else r=mid-1;
            }
            sum+=tmp-(sa[p][i]+h[i])+1; 
        }
        return sum;
    }
    
    void solve()
    {
        for(int i=1;i<=n;++i) val[i]=val[i-1]+w[a[i]];
        int l=0,r=1e7+1,mid,ans=-1;
        long long tmp;
        while(l<=r)
        {
            mid=l+r>>1;
            tmp=check(mid);
            if(tmp>=m) 
            {
                ans=mid;
                r=mid-1;
            }
            else l=mid+1;
        }
        if(ans==1e7+1) ans=-1;
        printf("%d
    ",ans);
    }
    
    int main()
    {
        int T;
        scanf("%d",&T);
        while(T--)
        {
            scanf("%d%d",&n,&m);
            scanf("%s",ch+1);
            for(int i=1;i<=n;++i) a[i]=ch[i]-'a'+1;
            for(int i=1;i<=26;++i) scanf("%d",&w[i]); 
            presa();
            solve();    
        }        
    }
    作者:xxy
    本文版权归作者和博客园共有,转载请用链接,请勿原文转载,Thanks♪(・ω・)ノ。
  • 相关阅读:
    Abstract与Virtual
    List 常用方法
    控制数据采样分布 计算概念
    中期答辩感想
    软件开发之团队理解
    详细设计理解
    竞赛系统需求分析
    软件代码规范之理解
    AngularJS 整理资料
    AngularJS合集
  • 原文地址:https://www.cnblogs.com/TheRoadToTheGold/p/15142098.html
Copyright © 2011-2022 走看看