【bzoj4310】跳蚤

  • 题解:

    • 读了半个小时题。。。首先明确题意:求$S$分成至多$k$个串,每个串的子串的最大字典序的最大字典序(要选两次最大)最小
    • 求出sa和ht,本质不同的子串的个数等于总个数减去$sa$里面前后相同的个数:$sum = \sum len - sa[i] - ht[i] $
    • 因为在后缀数组中本质相同的子串一定出现在$sa$的连续一段的$lcp$里;
    • 二分本质不同的串为第mid个,可以利用上面的结论$O(n)$求出第mid个串是什么;
    • 从后往前贪心,能不放就不放,比较两个串可以先求$lcp$再$O(1)$比较
    • 所以是$O(N \ logN)$的
       1 #include<cstdio>
       2 #include<iostream>
       3 #include<cstring>
       4 #define Run(i,l,r) for(int i=l;i<=r;i++)
       5 #define Don(i,l,r) for(int i=l;i>=r;i--)
       6 using namespace std;
       7 typedef long long ll;
       8 const int N=100010;
       9 int n,k,m,sa[N],ht[N],rk[N],c[N],x[N],y[N],b[N],f[N][20],lg[N],ansl,ansr;
      10 char s[N];
      11 void build_sa(){
      12     m=128;
      13     for(int i=0;i<m;i++) c[i]=0;
      14     for(int i=0;i<n;i++) c[x[i]=s[i]]++;
      15     for(int i=1;i<m;i++) c[i]+=c[i-1];
      16     for(int i=n-1;~i;i--) sa[--c[x[i]]]=i;
      17     int p;
      18     //for(int i=0;i<n;i++) cout<<sa[i]<<endl;cout<<endl;
      19     for(int k=1;k<n;k<<=1){
      20         p=0;
      21         for(int i=n-k;i<n;i++) y[p++]=i;
      22         for(int i=0;i<n;i++) if(sa[i]>=k)y[p++]=sa[i]-k;
      23         for(int i=0;i<m;i++) c[i]=0;
      24         for(int i=0;i<n;i++) c[x[y[i]]]++;
      25         for(int i=1;i<m;i++) c[i]+=c[i-1];
      26         for(int i=n-1;~i;i--) sa[--c[x[y[i]]]]=y[i];
      27         swap(x,y); p=1;x[sa[0]]=0;
      28         for(int i=1;i<n;i++) x[sa[i]]=(y[sa[i]]==y[sa[i-1]]&&y[sa[i]+k]==y[sa[i-1]+k])?p-1:p++;
      29         if(p>=n) break; 
      30         m=p+1;
      31     //    for(int i=0;i<n;i++) cout<<sa[i]<<endl;cout<<endl;
      32     }
      33 }////////////////
      34 void build_ht(){
      35     for(int i=0;i<n;i++)rk[sa[i]]=i;
      36     for(int i=0,k=0,j;i<n;ht[rk[i++]]=k)
      37     for(k?k--:0,j=sa[rk[i]-1];rk[i]&&s[i+k]==s[j+k];k++);
      38 }/////
      39 void rmq(){
      40     lg[1]=0;for(int i=2;i<=n;i++)lg[i]=lg[i>>1]+1;
      41     for(int i=0;i<=n;i++) f[i][0]=ht[i];
      42     for(int i=1;i<17;i++)
      43     for(int j=0;j+(1<<i)<=n;j++)
      44     f[j][i]=min(f[j][i-1],f[j+(1<<i-1)][i-1]);
      45 }////
      46 int lcp(int x,int y){
      47     if(x==y)return n-x;////
      48     x=rk[x],y=rk[y]; 
      49     if(x>y) swap(x,y);
      50     int t=lg[y-x];
      51     int tmp;
      52     tmp=min(f[x+1][t],f[y-(1<<t)+1][t]);///
      53 //    printf("%d %d %d\n",x,y,tmp);
      54     return tmp;
      55 }/////
      56 int l,r,len;
      57 void find(ll k){
      58     int i;
      59     for(i=1;i<=n;k-=b[i++])////
      60     if(b[i]>=k){l=sa[i],r=sa[i]+ht[i]+k-1;len=r-l+1;break;}///
      61     //printf("%d\n",i);
      62 }/////
      63 bool ask(int L,int R){
      64     int Len=R-L+1;
      65     int t=min(min(len,Len),lcp(l,L));
      66     if(t==Len&&t<=len) return 1;
      67     if(t==len) return 0;
      68     return s[l+t]>s[L+t];////
      69 }////
      70 bool check(){
      71     int i,j,cnt=0;
      72     for(i=j=n-1;~i;i=j,cnt++){
      73         while(~j&&ask(j,i))j--;
      74         if(j==i||cnt>k)return 0;
      75       //    printf("%d\n",j);
      76     }
      77 //    printf("\n");
      78     return cnt<=k;
      79 }/////
      80 int main()
      81 {    //freopen("bzoj4310.in","r",stdin);
      82     //freopen("bzoj4310.out","w",stdout);
      83     scanf("%d",&k);
      84     scanf("%s",s); n=strlen(s); s[n++]=0;
      85     build_sa(); build_ht(); rmq();//
      86     ll L=1,R=0,mid;  n--;
      87     for(int i=1;i<=n;i++) R+=(b[i]=n-sa[i]-ht[i]);///
      88     while(L<=R){ 
      89         find(mid=L+R>>1);
      90         if(check())ansl=l,ansr=r,R=mid-1;else L=mid+1;//
      91     }
      92     for(int i=ansl;i<=ansr;i++)putchar(s[i]);//
      93     return 0;
      94 }//by tkys_Austin;
      bzoj4310

       

 

posted @ 2019-01-04 21:48  大米饼  阅读(204)  评论(0编辑  收藏  举报