码迷,mamicode.com
首页 > 编程语言 > 详细

[poj 1743] Musical Theme 后缀数组 or hash

时间:2020-05-12 00:02:34      阅读:84      评论:0      收藏:0      [点我收藏+]

标签:连续   The   最长公共前缀   时长   否则   数字   sign   题解   hub   

Musical Theme

题意

给出n个1-88组成的音符,让找出一个最长的连续子序列,满足以下条件:

  1. 长度大于5
  2. 不重叠的出现两次(这里的出现可以经过变调,即这个序列的每个数字全都加上一个整数x)

思路

我们处理一下这个所谓的“变调”:令\(a[i]=a[i+1]-a[i]\),这样就转化成了找最长的出现至少两次的不重叠子串。(这时长度变为n-1)

两种做法:1.二分+ hash 2. 二分+后缀数组

使用hash的时候,对于当前二分的长度x。

我们从x开始遍历到n,如果[i-x+1,i]的hash值已经出现在i-x之前过,当前长度就可以,因为这个hash值是unsigned long long 范围,我们要使用map来判断,然后就会发现超时。

百度时候发现,题解的map是自己写的。

struct hashmap
{
    int head[Hash],next[N],sz,f[N];
    /*
    使用邻接表把%Hash相同的数字串起来
    state[i]放的是具体的值
    f[i]放的是值的下标
    sz是存的数字的数量
    */
    ull state[N];
    void init()
    {
        sz=0;
        memset(head,-1,sizeof(head));
    }
    int add(ull val,int id)
    {
        int now=val%Hash;
        for(int i=head[now]; i!=-1; i=next[i])
        {
            if(val==state[i])//val已经出现过,返回第一次出现的下标
                return f[i];
        }
        /*val没有出现,加入到hashmap中*/
        state[sz]=val;
        f[sz]=id;
        next[sz]=head[now];
        head[now]=sz++;
        return f[sz-1];
    }
} mp;

使用后缀数组。

引自[后缀数组——处理字符串的有力工具]----罗穗骞

先二分答案,把题目变成判定性问题:判断是否存在两个长度为k的子串是相同的,且不重叠。解决这个问题的关键还是利用height数组。把排序后的后缀分成若干组,其中每组的后缀之间的height值都不小于k。例如,字符串为“aabaaaab”,当k=2时,后缀分成了4组,如图所示。

技术图片

容易看出,有希望成为最长公共前缀不小于k的两个后缀一定在同一组。然后对于每组后缀,只须判断每个后缀的sa值的最大值和最小值之差是否不小于k。如果有一组满足,则说明存在,否则不存在。整个做法的时间复杂度为O(nlogn)。本文中利用height值对后缀进行分组的方法很常用,请读者认真体会

后缀数组代码

// #include <bits/stdc++.h>
#include<stdio.h>
#include<algorithm>
#include<string.h>
#define pb push_back
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
const int N = 1e5 + 10;
const int mod = 1e9 + 7;
const int inf = 0x3f3f3f3f;

int cnt[N],oldrk[N],rk[N],sa[N],pos[N],ht[N];
int n,m;
int arr[N];

bool cmp(int x,int y,int k)
{
    return oldrk[x]==oldrk[y]&&oldrk[x+k]==oldrk[y+k];
}
void getsa()
{
    memset(cnt,0,sizeof(cnt));
    m=200;
    for(int i=1; i<=n; i++)
        ++cnt[rk[i]=arr[i]];
    for(int i=1; i<=m; i++)
        cnt[i]+=cnt[i-1];
    for(int i=n; i; i--)
        sa[cnt[rk[i]]--]=i;
    for(int k=1; k<=n; k<<=1)
    {
        int num=0;
        for(int i=n-k+1; i<=n; i++)
            pos[++num]=i;
        for(int i=1; i<=n; i++)
        {
            if(sa[i]>k)
                pos[++num]=sa[i]-k;
        }
        memset(cnt,0,sizeof(cnt));
        for(int i=1; i<=n; i++)
            ++cnt[rk[i]];
        for(int i=1; i<=m; i++)
            cnt[i]+=cnt[i-1];
        for(int i=n; i; i--)
            sa[cnt[rk[pos[i]]]--]=pos[i];
        num=0;
        memcpy(oldrk,rk,sizeof(rk));
        for(int i=1; i<=n; i++)
            rk[sa[i]]=cmp(sa[i],sa[i-1],k)?num:++num;
        if(m==n)
            break;
        m=num;
    }
    for(int i=1; i<=n; i++)
        rk[sa[i]]=i;
    int k=0;
    for(int i=1; i<=n; i++)
    {
        if(k)
            --k;
        while(arr[i+k]==arr[sa[rk[i]-1]+k])
            ++k;
        ht[rk[i]]=k;
    }
}
int judge(int x)
{
    int minn=sa[1],maxn=sa[1];
    for(int i=2; i<=n; i++)//精辟
    {
        if(ht[i]>=x)
        {
            maxn=max(maxn,sa[i]);
            minn=min(minn,sa[i]);
        }
        else
        {
            minn=sa[i];
            maxn=sa[i];
        }
        if(maxn-minn>x)
            return 1;
    }
    return 0;
}
int main()
{
    while(~scanf("%d",&n)&&n)
    {
        for(int i=1; i<=n; i++)
            scanf("%d",&arr[i]);
        for(int i=1; i<n; i++)
            arr[i]=arr[i+1]-arr[i]+100;
        n--;
        getsa();
        int l=4,r=n/2,ans=0;
        while(l<=r)
        {
            int mid=(l+r)/2;
            if(judge(mid))
            {
                ans=mid;
                l=mid+1;
            }
            else
                r=mid-1;
        }
        if(!ans)
            printf("0\n");
        else
            printf("%d\n",ans+1);
    }
    return 0;
}

hash代码

#include<algorithm>
#include<stdio.h>
#include<string.h>
#include<map>
#define pb push_back
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
const int N = 2e4 + 10;
const int mod = 1610612741;
const int inf = 0x3f3f3f3f;
const int Hash=10007;

ull hash1[N],bin[N];
int n,arr[N],brr[N];
ull get(int l,int r)
{
    return hash1[r]-hash1[l-1]*bin[r-l+1];
}
struct hashmap
{
    int head[Hash],next[N],sz,f[N];
    ull state[N];
    void init()
    {
        sz=0;
        memset(head,-1,sizeof(head));
    }
    int add(ull val,int id)
    {
        int now=val%Hash;
        for(int i=head[now]; i!=-1; i=next[i])
        {
            if(val==state[i])
                return f[i];
        }
        state[sz]=val;
        f[sz]=id;
        next[sz]=head[now];
        head[now]=sz++;
        return f[sz-1];
    }
} mp;
int judge(int x)
{
    mp.init();
    for(int i=x; i<n; i++)
    {
        if(mp.add(get(i-x+1,i),i)<i-x)
            return 1;
    }
    return 0;

}
int main()
{
    bin[0]=1;
    for(int i=1; i<=20000; i++)
        bin[i]=bin[i-1]*137;
    while(~scanf("%d",&n)&&n)
    {
        for(int i=1; i<=n; i++)
            scanf("%d",&arr[i]);
        for(int i=1;i<n;i++)
        {
            arr[i]=arr[i+1]-arr[i];
            hash1[i]=hash1[i-1]*137+arr[i];
        }
        int l=4,r=n/2-1,ans=0;
        while(l<=r)
        {
            int mid=(l+r)/2;
            if(judge(mid))
            {
                ans=mid;
                l=mid+1;
            }
            else
                r=mid-1;
        }
        if(!ans)
            printf("0\n");
        else
            printf("%d\n",ans+1);
    }
    return 0;
}

[poj 1743] Musical Theme 后缀数组 or hash

标签:连续   The   最长公共前缀   时长   否则   数字   sign   题解   hub   

原文地址:https://www.cnblogs.com/valk3/p/12872908.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!