• bzoj4310 跳蚤


     题目

      给你一个字符串,让你分成k个不相交子串,让所有子串里最大的那个子串字典序尽量小。问最小的子串是什么。

    题解

      显然串越大,可以成为一种划分方案的结果的可能也越大,可以考虑根据这个单调性二分。

      首先整个串里,先求出总共有多少个不同的串,这样就有了二分的上下界。

      然后考虑怎么check,有一个大家都知道的性质,一个串S若是T的子串,则S最大的子串肯定不大于T的最大字串。

      那么已经二分出了一个界,肯定可以根据这个性质,贪心地使得一个串在不超过界的情况下,越长越好。其实就是个常见的二分加贪心套路。

      主要怎么支持以上操作?可以用到后缀数组。。

      不同串的个数就是$sum n-sa[i]-height[i]$,这个不用证了吧。

      然后贪心线扫的时候注意由于后缀数组的性质要倒着扫。

    代码

      1 #include <iostream>
      2 #include <cstdio>
      3 #include <cstdlib>
      4 #include <ctime>
      5 #include <algorithm>
      6 #include <cstring>
      7 typedef long long ll;
      8 using namespace std;
      9 const int N=100005;
     10 char str[N];
     11 int n,last,k,x,y;
     12 int sa[N],rank[N],cnta[N],cntb[N],a[N],b[N],height[N],H[N],tsa[N],rmql[19][N],rmqr[19][N],Log[N];
     13 ll tot[N];ll low,high,mid;
     14 bool cmp(int x,int y)
     15 {
     16         return str[x]<str[y];
     17 }
     18 void getsa()
     19 {
     20         for (int i=1;i<=n;++i)   sa[i]=i;
     21         sort(sa+1,sa+n+1,cmp);
     22         rank[sa[1]]=1;
     23         for (int i=2;i<=n;++i)   rank[sa[i]]=rank[sa[i-1]]+(str[sa[i]]!=str[sa[i-1]]);
     24         for (int j=1;rank[sa[n]]<n;j<<=1)
     25         {
     26                 for (int i=1;i<=n;++i)   a[i]=rank[i],b[i]=i+j>n?0:rank[i+j];
     27                 for (int i=0;i<=n;++i)   cnta[i]=cntb[i]=0;
     28                 for (int i=1;i<=n;++i)   ++cnta[a[i]],++cntb[b[i]];
     29                 for (int i=1;i<=n;++i)   cnta[i]+=cnta[i-1],cntb[i]+=cntb[i-1];
     30                 for (int i=n;i;--i) tsa[cntb[b[i]]--]=i;
     31                 for (int i=n;i;--i) sa[cnta[a[tsa[i]]]--]=tsa[i];
     32                 rank[sa[1]]=1;
     33                 for (int i=2;i<=n;++i)
     34                         rank[sa[i]]=rank[sa[i-1]]+(a[sa[i]]!=a[sa[i-1]] || b[sa[i]]!=b[sa[i-1]]);
     35         }
     36         H[0]=0;
     37         for (int i=1;i<=n;++i)
     38                 if (rank[i]>1)
     39                 {
     40                         H[i]=H[i-1]?H[i-1]-1:0;
     41                         while (str[i+H[i]]==str[sa[rank[i]-1]+H[i]])    ++H[i];
     42                         height[rank[i]]=H[i];
     43                 }
     44         for (int i=1;i<=n;++i)   rmql[0][i]=rmqr[0][i]=height[i];
     45         for (int j=1;(1<<j)<=n;++j)
     46                 for (int i=1;i+(1<<j)-1<=n;++i)
     47                         rmql[j][i]=min(rmql[j-1][i],rmql[j-1][i+(1<<j-1)]);
     48         for (int j=1;(1<<j)<=n;++j)
     49                 for (int i=1<<j;i<=n;++i)
     50                         rmqr[j][i]=min(rmqr[j-1][i],rmqr[j-1][i-(1<<j-1)]);
     51         Log[1]=0;
     52         for (int i=2;i<=n;++i)   Log[i]=Log[i>>1]+1;
     53 }
     54 bool judge(int a,int b,int c,int d)
     55 {
     56         if (a==c)   return b>d;
     57         if (a<c) return 0;
     58         int L=Log[a-c],s;
     59         s=min(rmql[L][c+1],rmqr[L][a]);
     60         if (b>s) return 1;
     61         return b>d;
     62 }
     63 bool check(ll K)
     64 {
     65         for (int i=1;i<=n;++i)
     66                 if (tot[i]>=K)
     67                 {
     68                         x=i;
     69                         y=K-tot[i-1]+height[i];
     70                         break;
     71                 }
     72         last=n+1;int kth=1;
     73         for (int i=n;i;--i)
     74                 if (judge(rank[i],last-i,x,y))
     75                 {
     76                         if (judge(rank[i],1,x,y))   return 0;
     77                         ++kth;
     78                         last=i+1;
     79                         if (kth>k)   return 0;
     80                 }
     81         return 1;
     82 }
     83 int main()
     84 {
     85         scanf("%d",&k);
     86         scanf("%s",str+1);  n=strlen(str+1);        
     87         getsa();
     88         low=1;high=tot[1]=n-sa[1]+1;
     89         for (int i=2;i<=n;++i)
     90                 high+=n-sa[i]+1-height[i],
     91                 tot[i]=high;
     92         while (low<high)
     93         {
     94                 mid=(low+high)>>1;
     95                 if (check(mid)) high=mid;
     96                 else low=mid+1;             
     97         }
     98         for (int i=1;i<=n;++i)
     99                 if (tot[i]>=low)
    100                 {
    101                         x=i;
    102                         y=low-tot[i-1]+height[i];
    103                         break;
    104                 }
    105         for (int i=1;i<=y;++i)   putchar(str[sa[x]+i-1]);
    106         return 0;
    107  
    108 }
    View Code
  • 相关阅读:
    通信错误:(-1)[描述:无法解析路由器DDNS地址,请检查DDNS状态.] 解析办法
    小数量宽带用户的福音,Panabit 云计费easyradius 接口隆重发布,PA宽带计费系统
    送给那些经常问我如何设置360测速结果为电信的朋友,360测速模块原理简单分析
    Universal-Image-Loader(UIL)使用方法&流程图&源码分析 ----- 未完
    Android开发框架
    Android线程池的使用(未完)
    Android LruCache究竟是什么
    Java finally语句到底是在return之前还是之后执行?
    Android自定义图片加载框架
    Android 自定义View实现单击和双击事件
  • 原文地址:https://www.cnblogs.com/Bleacher/p/8492234.html
Copyright © 2020-2023  润新知