hdu 4898 LCP+贪心思维

题意:将一个字符串切成k块,使得字典序最大的那块最小。

ORZ  WJMZBMR,几行题解读了一天才懂。

快速比较两个子串的大小可以利用LCP(最长公共前缀),比较公共前缀的下一个字符的大小就够了。

利用这种思想,首先我们可以预处理所有子串的LCP(后缀数组+记录 O(2nlog(2n))+O(n*n),dp(O(4*n*n)))

然后将这些子串利用LCP按照字典序排序,开始二分答案。

二分的答案就是这K个块字典序的上限。假设以i作为起点,由于字典序上限已知,所以我们可以立刻求出i点最远能选到哪个点。

现在问题变成了:已知每一个点最远能跳R的距离,求是否存在一条路径,使得跳K次回到起点。

首先我们假设,每个点的R≠0,意思就是每个点都能向后跳,这样我们只需要用贪心的思想,枚举任意点为起点,然后向后能跳多少跳多少,若跳回来所花的次数T<=K,则为true 【由于每个点都能向后跳,则我们一定能够通过改变几个跳跃,使得T==K  若当前的点数

现在的问题就是,如果有某些点R==0怎么办,也就是它一步也不能向后跳,并且其它位置也不能跳到这个位置。所以我们想到,将这个点删除掉,并且,将所有受到影响的点全部减一,以前可以从 a向后跳3步,但现在 b (a<=b<=a+3)被删掉了,所以a只能挑2步了,这样最多迭代n次 处理之后,所有点的R都是不为0的了。

代码写的很挫。。。

#include 
#include 
#include
#include
#include
#include
using namespace std;
#define maxn 2005
char str[maxn];
int sa[maxn],t1[maxn],t2[maxn],c[maxn],n;
void suffix(int m)
{
    int *x=t1,*y=t2;
    for(int i=0; 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=k)y[p++]=sa[i]-k;
        for(int i=0; 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)break;
        m=p;
    }
}
int rank[maxn],height[maxn];
void getheight()
{
    int k=0;
    for(int i=0; ir)swap(l,r);
    l++;
    int k=floor(log(r-l+1.0)/log(2.0));
    return min(f[l][k],f[r+1-(1< v[maxn];
void debug(int pos)
{
    for(int i=a[pos].l;ifar;
bool cal()
{
    far.clear();
    for(int i=0;i=tn)
        {
            far.push_back(tn);
            continue;
        }
        else
        {
            if(str[i+LCP]i&&j+far[j]>=i+far.size()) far[j]--;
                }
                ok=1;
                far.erase(i+far.begin());
            }
        }
    }
    if(far.size()


你可能感兴趣的:(hdu 4898 LCP+贪心思维)