HDU 5030 Rabbit's String 后缀数组

【题目大意】

有一个只有小写字母的字符串,你要把其分成不超过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[n];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[n] = 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[n];
        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;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值