您的位置:首页 > 其它

HDU 5030 Rabbit's String 后缀数组

2014-09-24 16:56 330 查看
【题目大意】

有一个只有小写字母的字符串,你要把其分成不超过k个不重叠的连续子串。对于每个子串,求其字典序最大的子串(为了与前者区分,我们称为子串2)。所有子串2,有一个字典序最大的串,我们称为目标串。问,怎么分,使得目标串字典序最小,输出这个目标串。

【思路】

很有意思的一题。首先,目标串一定是原串的一个子串。那么可以考虑去二分目标串(即二分所有子串)。

这里有个问题,怎么确定原串的第k大串是什么?这个问题是HDU 5008的简化版,我在以前博客中有写。

现在剩下的问题,就是对于确定串S(这个串是原串的子串),使得它成为目标串,至少把原串分为几部分?

这里可以贪心求解,举个例子:使“ba”成为“bbaa”的目标串,至少分几部分。

记re[i]表示,以原串的第i个字符做起点,求一个最大re[i],使得原串的子串[i,re[i]]字典序小于等于目标串“ba”。

bbaa的re[]为

下标:0 1 2 3

re[  ]:0 2 3 3

我们从左到右,贪心地加字符,能加就加,不能加就隔断,最后有几段,就是分几部分。具体怎么贪心,看下面代码。

ans = 0;

temp = -1;

for(i = 0; i < n; i ++)

{

    if(re[i] < i)

        return INF;

    if(i > temp)

    {

        ans++;

        temp = re[i];

    }

    else

        temp = min(temp, re[i]);

}

return ans;

//#pragma comment(linker, "/STACK:102400000,102400000")
#include<cstdio>
#include<cstring>
#include<vector>
#include<queue>
#include<cmath>
#include<cctype>
#include<string>
#include<algorithm>
#include<iostream>
#include<ctime>
#include<map>
#include<set>
using namespace std;
#define MP(x,y) make_pair((x),(y))
#define PB(x) push_back(x)
typedef __int64 LL;
//typedef unsigned __int64 ULL;
/* ****************** */
const int INF = 100011122;
const double INFF = 1e100;
const double eps = 1e-8;
const int mod = 1000000007;
const int NN = 100010;
const int MM = 5000010;
/* ****************** */

char ss[NN];
int a[NN], re[NN];
LL sum[NN];

int wa[NN],wb[NN],wv[NN],wss[NN],sa[NN];
int rank[NN],height[NN];
int rmq[NN][20],long2[NN];

int cmp(int *r,int a,int b,int l)
{return r[a]==r[b]&&r[a+l]==r[b+l];}
//r[n-1]==0;
void da(int *r,int n,int m)
{
int i,j,p,*x=wa,*y=wb,*t;
for(i=0;i<m;i++)wss[i]=0;
for(i=0;i<n;i++)wss[x[i]=r[i]]++;
for(i=1;i<m;i++)wss[i]+=wss[i-1];
for(i=n-1;i>=0;i--)sa[--wss[x[i]]]=i;
for(j=1,p=1;p<n;j*=2,m=p)
{
for(p=0,i=n-j;i<n;i++)y[p++]=i;
for(i=0;i<n;i++)if(sa[i]>=j)y[p++]=sa[i]-j;

for(i=0;i<n;i++)wv[i]=x[y[i]];
for(i=0;i<m;i++)wss[i]=0;
for(i=0;i<n;i++)wss[wv[i]]++;
for(i=1;i<m;i++)wss[i]+=wss[i-1];
for(i=n-1;i>=0;i--)sa[--wss[wv[i]]]=y[i];

for(t=x,x=y,y=t,p=1,x[sa[0]]=0,i=1;i<n;i++)
x[sa[i]]=cmp(y,sa[i-1],sa[i],j)?p-1:p++;
}
return;
}

void calheight(int *r,int n)
{
int i,j,k=0;
for(i=1;i<=n;i++)rank[sa[i]]=i;
for(i=0;i<n;height[rank[i++]]=k)
for(k?k--:0,j=sa[rank[i]-1];r[i+k]==r[j+k];k++);
return;
}

void initRMQ(int n)
{
int i,j,en;
long2[1]=0;
for(i=2;i<=n;i++)
{
long2[i]=long2[i-1]+(i==(i&(-i)));
}
for(i=1;i<=n;i++)rmq[i][0]=height[i];
for(j=1;j<=long2
;j++)
{
en=n+1-(1<<j);
for(i=1;i<=en;i++)
rmq[i][j]=min(rmq[i][j-1],rmq[ i+(1<<(j-1)) ][j-1]);
}
}
int lcp(int i,int j)
{
i=rank[i],j=rank[j];
if(i>j)swap(i,j);
i++;
int k=long2[j-i+1];
int ans=min(rmq[i][k],rmq[ j+1-(1<<k) ][k]);
return ans;
}

int solve(int st,int l,int len,int n)
{
int i, temp, ans;
for(i = 0; i < n; i ++)
{
if(i==l)
{
re[i] = l + len - 1;
continue;
}

temp = lcp(i, l);
temp = min(temp, len);
if(rank[i] < st)
{
if(temp == len)
re[i] = i + temp - 1;
else
re[i] = n-1;
}
else
{
re[i] = i + temp - 1;
}
}
ans = 0;
temp = -1;
for(i = 0; i < n; i ++)
{
if(re[i] < i)
return INF;
if(i > temp)
{
ans++;
temp = re[i];
}
else
temp = min(temp, re[i]);
}
return ans;
}

int ok(LL k,int n)
{
if(k==0)return INF;

int st = lower_bound(sum+1, sum+1+n, k) - sum;
int l = sa[st];
int len = height[st] + k - sum[st-1];
return solve(st, l, len, n);
}

int main()
{
int n, k, i;
LL l, r, mid;
while(scanf("%d",&k)!=EOF)
{
if(k==0)break;

scanf("%s", ss);
n = strlen(ss);
for(i = 0; i < n; i ++)
{
a[i] = ss[i]- 'a' +1;
}
a
= 0;
da(a,n+1,40);
calheight(a,n);
initRMQ(n);

sum[0] = 0;
for(i = 1; i <= n; i ++)
{
sum[i] = n - sa[i] - height[i];
sum[i] += sum[i-1];
}

l = 0;
r = sum
;
while (l + 1 < r)
{
mid = (l + r)>>1;
if(ok(mid,n) <= k)
r = mid;
else
l = mid;
}

int st = lower_bound(sum+1, sum+1+n, r) - sum;
int ll = sa[st];
int rr = ll + height[st] + r - sum[st-1] - 1;

for(i = ll; i <= rr; i ++)
{
printf("%c",ss[i]);
}
puts("");
}
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: