题意
内存限制:256 MiB
时间限制:1000 ms
很久很久以前,森林里住着一群跳蚤。一天,跳蚤国王得到了一个神秘的字符串,它想进行研究。
首先,他会把串分成不超过 $k$ 个子串,然后对于每个子串 $S$,他会从 $S$ 的所有子串中选择字典序最大的那一个,并在选出来的 $k$
个子串中选择字典序最大的那一个。他称其为“魔力串”。现在他想找一个最优的分法让“魔力串”字典序最小。
$k \le 15$ , $|S| \le 10^5$
题解
利用 $SAM/SA$ 求出本质不同串个数,进行二分
对于 $mid$ ,利用 $SAM/SA$ 求出其对应的子串 $s$
因为一个串的最大字典序子串一定是其后缀(可以画个图理解一下,所以从后往前枚举,如果 $[i,last]$ 的字典序比 $s$ 大,则必须在 $[i-1,last]$ 分割, $i$ 作为新的一个 $last$
求字典序只需求出最长公共前缀,可以二分 $hash$ 或者 $SA$ 进行处理
我写的是 $SAM$ +二分 $hash$ ,效率 $O(nlogn-nlog^2n)$(我也不会分析了
代码
#include <bits/stdc++.h> #define K 793999 #define LL long long #define U unsigned LL using namespace std; const int N=2e5+5; int k,n,lst=1,sz=1,t[N],e[N],tp; LL s[N],f[N];U b[N],h[N][2]; char str[N],now[N]; struct SAM{ int link,len,nx[26]; }a[N]; void build(int x){ int np=++sz,p=lst; a[np].len=a[p].len+1; while(p && !a[p].nx[x]) a[p].nx[x]=np,p=a[p].link; if (!p) a[np].link=1; else{ int q=a[p].nx[x]; if (a[q].len==a[p].len+1) a[np].link=q; else{ int nq=++sz; a[nq]=a[q]; a[nq].len=a[p].len+1; a[q].link=a[np].link=nq; while(p && a[p].nx[x]==q) a[p].nx[x]=nq,p=a[p].link; } } lst=np; } void find(int u,LL v){ v-=f[u];if (!v) return; for (int j,i=0;i<26;i++) if ((j=a[u].nx[i])){ if (v>s[j]) v-=s[j]; else{ now[++tp]=i+'a',find(j,v); return; } } } U H(int l,int r,bool ty){ return h[r][ty]-h[l-1][ty]*b[r-l+1]; } bool cmp(int L,int R){ int d,l=0,r=min(tp,R-L+1); while(l<r){ d=(l+r+1)>>1; if (H(1,d,1)==H(L,L+d-1,0)) l=d; else r=d-1; } if (l==min(tp,R-L+1)) return tp>=R-L+1; return str[L+l]<now[l+1]; } bool J(){ int res=0; for (int l=n,r=n;l;){ while(cmp(l,r) && l) l--; if (l==r) return 0;res++;r=l; if (res>k) return 0; } return 1; } int main(){ scanf("%d%s",&k,str+1); b[0]=1;n=strlen(str+1); for (int i=1;i<=n;i++) build(str[i]-'a'),b[i]=b[i-1]*K, h[i][0]=h[i-1][0]*K+str[i]; for (int i=1;i<=sz;i++) e[a[i].len]++,f[i]=1; for (int i=1;i<=sz;i++) e[i]+=e[i-1];f[1]=0; for (int i=1;i<=sz;i++) t[e[a[i].len]--]=i; for (int j,i=sz;i;i--){ j=t[i];s[j]=f[j]; for (int l=0;l<26;l++) s[j]+=s[a[j].nx[l]]; } LL l=1,r=s[1],d;while(l<r){ d=(l+r)>>1;tp=0;find(1,d); for (int i=1;i<=tp;i++) h[i][1]=h[i-1][1]*K+now[i]; if (J()) r=d;else l=d+1; } tp=0;find(1,l); for (int i=1;i<=tp;i++) putchar(now[i]);putchar('\n'); return 0; }