hdu6704 2019CCPC网络选拔赛1003 K-th occurrence 后缀数组

题意:给你一个长度为n的字符串,有q个询问,每次询问一个子串s(l,r)第k次出现的位置,若子串出现次数少于k次输出-1.

解题思路:先把SA跑出来,然后对于每次询问可以由l和rank[]找到l在所有后缀中的排名,再用两次二分求出使得LCP(L,R)包含s(l,r)的最大区间[L,R],LCP可以借助height[]的性质和ST表求得,即[L,R]包含rank[l]且min{height[L+1],height[L+2],...,height[R]}>=r-l+1。现在问题就转化为了求[L,R]中第k大的sa[i],这个就是主席树经典操作了。

感觉这个做法挺容易想出来的,但是就是写起来有些麻烦,对SA不熟悉的话就容易写劈叉。

听机房大佬说还有后缀自动机+线段树合并的写法,先留个坑(填不填就不一定了)

AC代码:

#include<bits/stdc++.h>
using namespace std;

typedef long long ll;
const int maxn=2e5+5;
const int INF=0x3f3f3f3f;

char s[maxn];
int height[maxn],c[maxn],x[maxn],y[maxn],sa[maxn],rk[maxn];
void SA(int n){
    n++;
    int i,j,k,m=128;
    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){
      k=0;
      for(i=n-j;i<n;i++) y[k++]=i;
      for(i=0;i<n;i++) if(sa[i]>=j) y[k++]=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);
      m=0;
      x[sa[0]]=m++;
      for(i=1;i<n;i++){
          if(y[sa[i]]==y[sa[i-1]]&&y[sa[i]+j]==y[sa[i-1]+j]) x[sa[i]]=m-1;
          else x[sa[i]]=m++;
      }
      if(m>=n) break;
    }
    k=0;
    for(i=0;i<n;i++) rk[sa[i]]=i;
    for(i=0;i<n-1;i++){
      if(k) k--;
      j=sa[rk[i]-1];
      while(s[i+k]==s[j+k]) k++;
      height[rk[i]]=k;
    }

//    cout<<"sa:";for(int i=0;i<n;i++)cout<<sa[i]<<" ";cout<<endl;
//    cout<<"rk:";for(int i=0;i<n;i++)cout<<rk[i]<<" ";cout<<endl;
//    cout<<"h:";for(int i=1;i<n-1;i++)cout<<height[i]<<" ";cout<<endl;
//
//    for(int i=0;i<n;i++){
//        for(int j=sa[i];j<n;j++)putchar(s[j]);
//        putchar('\n');
//    }

}

int n,m;

int mi[maxn][20],lg[maxn];
void initRMQ()
{
    memset(mi,0x3f,sizeof(mi));
    lg[1]=0;
    for(int i=2;i<=n;i++)lg[i]=lg[i>>1]+1;
    for(int i=1;i<=n;i++)mi[i][0]=height[i];
    for(int j=1;j<=lg[n];j++)
        for(int i=1;i<=n;i++)
            mi[i][j]=min(mi[i][j-1],mi[i+(1<<(j-1))][j-1]);
}

int queryRMQ(int x,int y)
{
    int l=min(x,y)+1,r=max(x,y);
    int d=lg[r-l+1];
    //cout<<"llrr:"<<l<<" "<<r<<" "<<min(mi[l][d],mi[r-(1<<d)+1][d])<<endl;
    return min(mi[l][d],mi[r-(1<<d)+1][d]);;
}

int T[maxn],cnt;
int sum[maxn<<5],L[maxn<<5],R[maxn<<5];
inline int build(int l,int r)
{
    int rt=++cnt;
    sum[rt]=0;
    int mid=(l+r)/2;
    if(l<r){
        L[rt]=build(l,mid);
        R[rt]=build(mid+1,r);
    }
    return rt;
}

inline int update(int pre,int l,int r,int x)
{
    int rt=++cnt;
    L[rt]=L[pre];
    R[rt]=R[pre];
    sum[rt]=sum[pre]+1;
    int mid=(l+r)/2;
    if(l<r){
        if(x<=mid)L[rt]=update(L[pre],l,mid,x);
        else R[rt]=update(R[pre],mid+1,r,x);
    }
    return rt;
}

inline int query(int u,int v,int l,int r,int k)
{
    if(l>=r)return l;
    int mid=(l+r)/2;
    int x=sum[L[v]]-sum[L[u]];
    if(x>=k)return query(L[u],L[v],l,mid,k);
    else return query(R[u],R[v],mid+1,r,k-x);
}

int solve(int l,int r,int k)
{
    int len=r-l+1;
    int lp=rk[l],rp=rk[l];

    int ll=0,rr=rk[l],mid;
    int mii;
    while(ll<=rr){
        mid=(ll+rr)/2;
        mii=queryRMQ(mid,rk[l]);
        if(mii>=len)lp=mid,rr=mid-1;
        else ll=mid+1;
    }

    ll=rk[l]+1,rr=n;
    while(ll<=rr){
        mid=(ll+rr)/2;
        mii=queryRMQ(rk[l],mid);
        if(mii>=len)rp=mid,ll=mid+1;
        else rr=mid-1;
    }

//    cout<<"lr:"<<rk[l]<<" "<<lp<<" "<<rp<<endl;

    if(rp-lp+1<k)return -1;

    return query(T[lp-1],T[rp],0,n-1,k)+1;
}

int main()
{
//#ifndef ONLINE_JUDGE
//    freopen("in.txt","r",stdin);
//#endif
    int Case;
    scanf("%d",&Case);
    while(Case--){
        scanf("%d %d",&n,&m);
        scanf("%s",s);
        SA(n);

        initRMQ();

        cnt=0;
        T[0]=build(0,n-1);
        for(int i=1;i<=n;i++)T[i]=update(T[i-1],0,n-1,sa[i]);

        int l,r,k;
        while(m--){
            scanf("%d %d %d",&l,&r,&k);
            printf("%d\n",solve(l-1,r-1,k));
        }
    }
    return 0;
}

原文地址:https://www.cnblogs.com/zengzk/p/11403196.html

时间: 2024-10-15 20:16:48

hdu6704 2019CCPC网络选拔赛1003 K-th occurrence 后缀数组的相关文章

2019CCPC网络选拔赛 array(权值线段树)

2019CCPC网络选拔赛1002 HDU6703 题目大意: T个样例.给你一个长度为n的数组a,1≤a[i]≤n,a[i]各不相同.m个操作.ans初始为0.有两种操作: 操作1:给你t1.pos=t1^ans.把数组下标为pos的数,数值+1e7: 操作2:给你t2,t3.r=t2^ans,k=t3^ans.输出**与数组下标1~r的数不同**且**不小于k**的最小数.更新ans. 数据范围: 1≤T≤10,1≤n≤1e5,1≤m≤1e5,1≤a[i]≤n,a[i]各不相同,1≤pos≤

2019CCPC网络预选赛 1003 K-th occurrence 后缀自动机 + 二分 + 主席树

题意:给你一个长度为n的字符串,有m次询问,每次询问l到r的子串在原串中第k次出现的位置,如果没有输出-1.n, m均为1e5级别. 思路:后悔没学后缀数组QAQ,其实只要学过后缀数组这个题还是比较好想的.这个问题可以转化为有多少个后缀和后缀l的lcp长度大于等于r - l + 1.我们知道,在后缀数组中,两个后缀i, j的lcp是min(height[rank[j] + 1], height[rank[j] + 2], ....height[rank[i]]).那么,我们可以二分出一个最靠左的

2019 CCPC 网络赛第三题 K-th occurrence 后缀数组+划分树+ST表+二分

题意:给你一个长度为n的字符串,每次询问给出三个数:L , R , K,表示原串 L 到 R 的子串在原串第K次出现的首字母的位置 解题思路:对子串的大量操作,不难想到后缀数组(后缀树/后缀自动机不会,所以没想到),注意到子串s[L.....R]必然是某一个后缀的前缀,所以所有前缀是该子串的后缀的排名(即rank数组的值)必定连续,也就是说在后缀数组(sa数组)中,下标是连续的,那么就是求区间第K大了(因为sa数组的值代表的是在字符串中的位置)(这里区间第K大我用划分树求),至于这一段区间的起点

HDU - 6704 K-th occurrence (后缀数组+主席树/后缀自动机+线段树合并+倍增)

题意:给你一个长度为n的字符串和m组询问,每组询问给出l,r,k,求s[l,r]的第k次出现的左端点. 解法一: 求出后缀数组,按照排名建主席树,对于每组询问二分或倍增找出主席树上所对应的的左右端点,求第k大的下标即可. 1 #include<bits/stdc++.h> 2 using namespace std; 3 typedef long long ll; 4 const int N=1e5+10,mod=998244353; 5 char buf[N]; 6 int s[N],sa[

hdu6075 2019CCPC网络选拔赛1004 path

题意:给定一个带权有向图,有q组询问,每次询问在有向图的所有路径中,第k小的路径权值 解题思路:因为k最大只有5e4,考虑暴力搜索出前maxk小的路径并用数组记录权值,然后就可以O(1)查询. 具体实现:暴力搜索时可以借助Dijkstra最短路的思想,即用已知的最短路更新得出新的最短路.先将所有的边都装进一个multiset里面,然后每次将multiset里的首元素取出,作为新的答案,然后再用它来更新新的最短路,这样不断扩散的话就可以得到答案. 但是,这样可能会TLE或MLE,考虑再加加优化,首

2015沈阳网络赛1003 Minimum Cut 树链剖分 数组维护前缀和进行区间增减

2015沈阳网络赛1003  Minimum Cut   树链剖分 数组维护前缀和进行区间增减 Minimum Cut Time Limit: 3000/2000 MS (Java/Others)    Memory Limit: 65535/102400 K (Java/Others)Total Submission(s): 0    Accepted Submission(s): 0 Problem Description Given a simple unweighted graph G 

2016 ccpc 网络选拔赛 F. Robots

Robots Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/65536 K (Java/Others)Total Submission(s): 60    Accepted Submission(s): 13 Problem Description QXJ has N robots on the plane, the i-th is at (xi,yi), numbereded 1 to N. Every robot

HDU 5833 Zhu and 772002(高斯消元)——2016中国大学生程序设计竞赛 - 网络选拔赛

传送门 Zhu and 772002 Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/65536 K (Java/Others)Total Submission(s): 48    Accepted Submission(s): 16 Problem Description Zhu and 772002 are both good at math. One day, Zhu wants to test the abili

HDU 5832 A water problem(取模~)—— 2016中国大学生程序设计竞赛 - 网络选拔赛

传送门 A water problem Time Limit: 5000/2500 MS (Java/Others)    Memory Limit: 65536/65536 K (Java/Others)Total Submission(s): 60    Accepted Submission(s): 37 Problem Description Two planets named Haha and Xixi in the universe and they were created wit