后缀数组 最长不可重叠重复子串问题

来源:互联网 发布:感动一个人 知乎 编辑:程序博客网 时间:2024/06/06 03:39

时间限制:5000ms
单点时限:1000ms
内存限制:256MB
描述

小Hi平时的一大兴趣爱好就是演奏钢琴。我们知道一个音乐旋律被表示为长度为 N 的数构成的数列。小Hi在练习过很多曲子以后发现很多作品自身包含一样的旋律。

旋律可以表示为一段连续的数列,相似的旋律在原数列不可重叠,比如在1 2 3 2 3 2 1 中 2 3 2 出现了一次,2 3 出现了两次,小Hi想知道一段旋律中出现次数至少为两次的旋律最长是多少?

解题方法提示

输入

第一行一个整数 N。1≤N≤100000

接下来有 N 个整数,表示每个音的数字。1≤数字≤1000

输出

一行一个整数,表示答案。

样例输入
8
1 2 3 2 3 2 3 1
样例输出
2
小Ho:这一次的问题该如何解决呢?

小Hi:嗯,这次的问题被称为最长不可重叠重复子串问题。

小Ho:和上次的问题好像啊,但是这一次是不可以重叠的,直接使用上次的算法似乎行不通喔。

小Hi:是的。问题的关键就出在直接用 height 数组不能保证两后缀不重叠,我们得换个思路考虑。

小Ho:可不可以二分答案,转化成判定问题呢?

小Hi:是个好思路,这的确是可行的。我们先二分一个k,表示我们假设串中存在长度为k的不可重叠重复子串。

小Ho:嗯,就是这个意思。

小Hi:存在长度为k的不可重复子串等价于存在两个后缀有长度为k的公共前缀(这里没有要求不重叠)。我们检查 height 数组中有哪些值 ≥ k。并且如果有连续的height值 ≥ k,就把对应的后缀分在同一组。这样就保证了该组中所有后缀两两之间的最长公共前缀都是不小于k的。

我们以样例为例,看一下k=2和k=3的情况。

x i height k=2 k=3
1 8 0
1 2 3 2 3 2 3 1 1 1
2 3 1 6 0
2 3 2 3 1 4 2 >=2
2 3 2 3 2 3 1 2 4 >=2 >=3
3 1 7 0
3 2 3 1 5 1
3 2 3 2 3 1 3 3 >=2 >=3
可以看出,当k=2时,”231”和”23231”的公共前缀大于等于k,”23231”和”2323231”的公共前缀也大于等k,所以这3个排名连续的后缀会被分到一组。同理”3231”和”323231”也会被分到一组。

对于k=3,”23231”和”2323231”分到一组,”3131”和”323231”分到一组。

小Ho:我知道了!

小Hi:对,没错!下面我们要看看能不能找出不重叠的重复子串。对于每一组,我们检查这些后缀对应的sa值(也就是后缀起点在原串中的位置i)。如果max{sa} - min{sa} >= k,那么就说明我们能找出一组不重叠的重复子串。

例如对于k=3,”23231”和”2323231”的sa值是4和2,”3131”和”323231”这一组的sa值是5和3,差值都不满足大于等于3,所以找不出不重叠的。

对于k=2,第一组max{sa}-min{sa}=6-2=4满足大于等于2,所以能找出不重叠的。

因为只用统计两次 所以 不同统计次数

#include <bits/stdc++.h>using namespace std;const int MAXN = 101000;int t1[MAXN],t2[MAXN],c[MAXN];int n;bool cmp(int *r,int a,int b,int l){    return r[a]==r[b]&&r[a+l]==r[b+l];}void da(int str[],int sa[],int ra[],int height[],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++) ra[sa[i]]=i;    for(i=0;i<n;i++)     {        if(k) k--;        j=sa[ra[i]-1];        while(str[i+k]==str[j+k])k++;        height[ra[i]]=k;    }}int ra[MAXN],height[MAXN];int sa[MAXN],num[MAXN];bool ok(int k){    int ans=0;    for(int i=1;i<=n;i++)    {        if(height[i]<k) continue;        int maxx=max(sa[i-1],sa[i]),minn=min(sa[i-1],sa[i]);        int j=i;        while(height[j+1]>=k&&j<=n)        {            j++,maxx=max(maxx,sa[j]),minn=min(minn,sa[j]);        }        if(maxx-minn>=k)            return 1;    }    return 0;}int main(){    scanf("%d",&n);    for(int i=0;i<n;i++) scanf("%d",&num[i]);    num[n]=0;    da(num,sa,ra,height,n,1001);    int l=0,r=n;    while(l<=r)    {        int mid=(l+r)>>1;        if(ok(mid))            l=mid+1;        else r=mid-1;    }    printf("%d\n",l-1 );}
阅读全文
0 0
原创粉丝点击