I - 后缀数组二·重复旋律2 HihoCoder - 1407

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

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

解题方法提示

×
解题方法提示
小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,所以能找出不重叠的。

我们给出如下c++代码:

bool check(int K)
{
for(int i=1;i<=n;i++)
if(height[i]< K)
{
minsa=sa[i];
maxsa=sa[i];
}
else
{
minsa=min(minsa,sa[i]);
maxsa=max(maxsa,sa[i]);
if(maxsa-minsa>=K)return true;
}
return false;
}
小Ho:哈哈,不难嘛,我马上去实现一发!

Close
输入
第一行一个整数 N。1≤N≤100000

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

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

Sample Input
8
1 2 3 2 3 2 3 1
Sample Output
2

数据很水,把上面给的函数放进二分就好了,如果max和min的sa值相差大于k的话就说明存在大于等于k的串

#include<stdio.h>
#include<string.h>
#include<iostream>
#include<algorithm>
using namespace std;
const int MAXN =(int)1e6+10;
int wa[MAXN],wb[MAXN],wv[MAXN],we[MAXN],rk[MAXN];
int cmp(int *r,int a,int b,int l){return r[a]==r[b]&&r[a+l]==r[b+l];}
void build_sa(int *r,int *sa,int n,int m){
    int i,j,p,*x=wa,*y=wb,*t;
    for(i=0;i<m;i++)we[i]=0;
    for(i=0;i<n;i++)we[x[i]=r[i]]++;
    for(i=1;i<m;i++)we[i]+=we[i-1];
    for(i=n-1;i>=0;i--)sa[--we[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++)we[i]=0;
        for(i=0;i<n;i++)we[wv[i]]++;
        for(i=1;i<m;i++)we[i]+=we[i-1];
        for(i=n-1;i>=0;i--)sa[--we[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++;
    }
}
int height[MAXN];
void calheight(int *r,int *sa,int n){
    int i,j,k=0;
    for(i=1;i<=n;i++)rk[sa[i]]=i;
    for(i=0;i<n;height[rk[i++]]=k){
        for(k?k--:0,j=sa[rk[i]-1];r[i+k]==r[j+k];k++);
    }
}
int sa[MAXN],a[MAXN];
//sa是从1-n,开始的值是0,rk是从0- n-1,开始的值是1
int n,k;
bool check(int K)
{
    int minsa=1e5,maxsa=1e5;
    for(int i=0;i<=n;i++)
    {
        if(height[i]< K)
        {
            minsa=sa[i];
            maxsa=sa[i];
        }
        else
        {
            minsa=min(minsa,sa[i]);
            maxsa=max(maxsa,sa[i]);
            if(maxsa-minsa>=K)return true;
        }
    }
    return false;
}
int main()
{
    scanf("%d",&n);
    for(int i=0;i<n;i++)
        scanf("%d",&a[i]);
    build_sa(a,sa,n+1,2000);
    calheight(a,sa,n);
    int low=0,high=1e5,mid,ans;
    while(high>=low)
    {
        mid=low+high>>1;
        if(check(mid))
            ans=mid,low=mid+1;
        else
            high=mid-1;
    }
    printf("%d\n",ans);
    return 0;
}

猜你喜欢

转载自blog.csdn.net/tianyizhicheng/article/details/82020446