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


    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组,如图所示。

    20161026120305167

    容易看出,有希望成为最长公共前缀不小于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
    ");
            else
                printf("%d
    ",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
    ");
            else
                printf("%d
    ",ans+1);
        }
        return 0;
    }
    
    
  • 相关阅读:
    [置顶] 宏途_LCD调试流程.
    字典树的数据结构及基本算法的实现
    uva 10714 Ants(贪心)
    paip.输入法编程---增加码表类型
    chomp方法
    ios 限制输入长度
    我所理解的设计模式(C++实现)——策略模式(Strategy Pattern)
    Android用户界面 UI组件--AdapterView及其子类(一) ListView及各种Adapter详解
    C#系列教程——switch定义及使用
    局域网内linux由ip反解析主机名
  • 原文地址:https://www.cnblogs.com/valk3/p/12872908.html
Copyright © 2020-2023  润新知