POJ3261 Milk Patterns 后缀数组+二分

这道题据说是后缀数组的水题emmmmm...然而我这个菜鸡什么都不会啊555555...

题意:找出至少出现k次的可重叠的最长子串的长度

之前做POJ1743有一点点基础,通过这道题对各个数组的理解更深入了。画了一个后缀树,好理解多了:(参考博客Orz:https://www.cnblogs.com/jinkun113/p/4743694.html

POJ3261 Milk Patterns 后缀数组+二分

据说要先离散化,但是不离散化也能过,我写的没有离散化的。思路就是二分长度,判断出现次数。即利用height值进行分组,再判断有没有哪一组的后缀数量(num初始化为1)>=k。我还是理解不了啊啊啊啊55555...记录下来回头再多看看QAQ...

附上参考博客Orz:https://blog.csdn.net/libin56842/article/details/46236377

有人总结过(附上总结博客Orz:https://blog.csdn.net/brandohero/article/details/41938695):使用后缀数组解题时,遇到“最长”,除了特殊情况外(如可重叠最长重复子串,即height数组最大值),一般需要二分答案,利用height值进行分组。

附上AC代码:

#include<cstdio>
#include<cstring>
#include<cmath>
#include<iostream>
#include<algorithm>
#include<vector>
#include<map>
#include<set>
#include<stack>
#include<queue>
using namespace std;
#define ll long long
typedef pair<int,int>pp;
#define mkp make_pair
#define pb push_back
const int INF=0x3f3f3f3f;
const ll MOD=1e9+(ll)7;

const int MAX=20005;
int n,k;

int t1[MAX],t2[MAX],c[MAX];
bool cmp(int *r,int a,int b,int l)
{
    return r[a]==r[b]&&r[a+l]==r[b+l];
}
int sa[MAX];
int ran[MAX];
int height[MAX];
void da(int str[],int n,int m)
{
    n++;
    int i,j,p,*x=t1,*y=t2;
    for(i=0;i<m;i++) c[i]=0;
    for(i=0;i<n;i++) c[x[i]=str[i]]++;
    for(i=1;i<m;i++) c[i]+=c[i-1];
    for(i=n-1;i>=0;i--) sa[--c[x[i]]]=i;
    for(j=1;j<=n;j<<=1)
    {
        p=0;
        for(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<m;i++) c[i]=0;
        for(i=0;i<n;i++) c[x[y[i]]]++;
        for(i=1;i<m;i++) c[i]+=c[i-1];
        for(i=n-1;i>=0;i--) sa[--c[x[y[i]]]]=y[i];
        swap(x,y);
        p=1;x[sa[0]]=0;
        for(i=1;i<n;i++)
            x[sa[i]]=cmp(y,sa[i-1],sa[i],j)?p-1:p++;
        if(p>=n)
            break;
        m=p;
    }
    int k=0;
    n--;
    for(i=0;i<=n;i++)
        ran[sa[i]]=i;
    for(i=0;i<n;i++)
    {
        if(k)
            k--;
        j=sa[ran[i]-1];
        while(str[i+k]==str[j+k])
            k++;
        height[ran[i]]=k;
    }
}

bool check(int x)
{
    int num=1;//要初始化为1(不理解QAQ...)
    for(int i=2;i<=n;i++)
    {
        if(height[i]>=x)
            num++;
        else//重新分组
            num=1;
        if(num>=k)
        {
            //cout<<"x="<<x<<" num="<<num<<endl;
            return true;//x偏小
        }
    }
    //cout<<"x="<<x<<" num="<<num<<endl;
    return false;
}

int r[MAX];
int main()
{
    while(scanf("%d%d",&n,&k)==2)
    {
        int m=0;
        for(int i=0;i<n;i++)
        {
            scanf("%d",&r[i]);
            m=max(m,r[i]);
        }
        r[n]=0;
        memset(sa,0,sizeof(sa));
        memset(ran,0,sizeof(ran));
        memset(height,0,sizeof(height));
        da(r,n,m+1);
        /*for(int i=1;i<=n;i++)
            cout<<"i="<<i<<" sa="<<sa[i]<<endl;
        for(int i=1;i<=n;i++)
            cout<<"i="<<i<<" sa[i-1]="<<sa[i-1]<<" sa[i]="<<sa[i]<<" height="<<height[i]<<endl;*/

        int l=0,r=n,mid;
        while(l<=r)
        {
            mid=(l+r)/2;
            //cout<<"l="<<l<<" r="<<r<<" mid="<<mid<<endl;
            if(check(mid))
                l=mid+1;
            else
                r=mid-1;
        }
        l--;
        printf("%d\n",l);
    }
	return 0;
}