题目
给你一个字符串,让你分成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 }