标签:blog io os ar for sp 2014 问题 on
题目大意:
要求将一个长串分解成最多k个子串,使得分开的n个串的字典序最大的那一个子串的字典序最小。
思路分析:
要最大的最小,不难想到二分的。
我们二分出原串中的第rk大子串就是目标串。
现在就是怎么判断这个串满足要求,也就是我们如何分其他部分,使之成为字典序最大的一个。
我们可以通过rk轻易的找到这是哪一个串,假设它处在sa[t]中。
那么可以知道 在 sa数组中t以前的子串的字典序都是比目标串小的。
而后面会有比sa大的,我们就要分解这些串。
我们从t 扫描 到n的height ,如果有一个height为0了,那么肯定是不行的,因为这个串的第一个都大。然后考虑不是0 的情况,那么就是说在这个子串的后面再加一个原串的下一个字母,就会比目标串大。所以这里就要切割,使之比目标串小。
然后最后判断你分割了多少个串,是否比k小。
#include <cstdio> #include <iostream> #include <cstring> #include <algorithm> #define maxn 100005 using namespace std; typedef long long ll; int sa[maxn],t1[maxn],t2[maxn],c[maxn]; void suffix(const char *str,int n,int m) { int *x=t1,*y=t2; for(int i=0; i<m; i++)c[i]=0; for(int i=0; i<n; i++)c[x[i]=str[i]]++; for(int i=1; i<m; i++)c[i]+=c[i-1]; for(int i=n-1; i>=0; i--)sa[--c[x[i]]]=i; for(int k=1; k<=n; k<<=1) { int p=0; for(int i=n-k; i<n; i++)y[p++]=i; for(int i=0; i<n; i++)if(sa[i]>=k)y[p++]=sa[i]-k; for(int i=0; i<m; i++)c[i]=0; for(int i=0; i<n; i++)c[x[y[i]]]++; for(int i=0; i<m; i++)c[i]+=c[i-1]; for(int i=n-1; i>=0; i--)sa[--c[x[y[i]]]]=y[i]; swap(x,y); p=1; x[sa[0]]=0; for(int i=1; i<n; i++) x[sa[i]]=y[sa[i-1]]==y[sa[i]]&&y[sa[i-1]+k]==y[sa[i]+k]?p-1:p++; if(p>=n)break; m=p; } } int rank[maxn],height[maxn]; void getheight(const char *str,int n) { int k=0; for(int i=0; i<n; i++)rank[sa[i]]=i; for(int i=0; i<n; i++) { if(k)k--; if(!rank[i])continue; int j=sa[rank[i]-1]; while(str[i+k]==str[j+k])k++; height[rank[i]]=k; } } char str[maxn]; ll sum[maxn]; int n,k; int cc[maxn]; bool ok(ll rk) { int t=lower_bound(sum+1,sum+n,rk)-sum; int l=sa[t]; int r=sa[t]+rk-sum[t-1]+height[t]-1; int len=r-l+1;//找到目标串 for(int i=0;i<n;i++)cc[i]=-1;//如果cc不等于-1,就代表在i切下了一个子串 [i,i+len]. if(sa[t]+len<n-1) cc[sa[t]]=len;//如果这个串是不用切割的,也就是他的结尾就是原串的末尾 for(int i=t+1;i<n;i++) { len=min(len,height[i]); if(len==0)return false; if(sa[i]+len<n-1) cc[sa[i]]=len; } int ans=0; r=n; for(int i=0;i<n;i++) {//在切割的时候有些问题,就是前面的时候已经切割到自己这一段区间,就可以跳过。 if(i==r) { ans++; r=n; if(ans>=k)return false; } if(cc[i]!=-1) { r=min(r,i+cc[i]); } } return ans<k;//分解成了ans+1段 } int main() { while(scanf("%d",&k)!=EOF && k) { scanf("%s",str); n=strlen(str)+1; suffix(str,n,128); getheight(str,n); sum[0]=0; for(int i=1;i<n;i++) sum[i]=sum[i-1]+(n-sa[i]-height[i]-1); ll l=1,r=n*n,mid,ans=1; while(l<=r) { mid=(l+r)>>1; if(ok(mid)) { ans=mid; r=mid-1; } else l=mid+1; } int t=lower_bound(sum+1,sum+n,ans)-sum; int ll=sa[t]; int rr=sa[t]+ans-sum[t-1]+height[t]-1; for(int i=ll;i<=rr;i++) printf("%c",str[i]); puts(""); } return 0; }
Hdu 5030 Rabbit's String (后缀数组)
标签:blog io os ar for sp 2014 问题 on
原文地址:http://blog.csdn.net/u010709592/article/details/39553097