poj 1743 Musical Theme(后缀数组)

题意:有N(1 <= N <=20000)个音符的序列来表示一首乐曲,每个音符都是1..88范围内的整数,现在要找一个重复的主题。“主题”是整个音符序列的一个子串,它需要满足如下条件:

    1.长度至少为5个音符。

    2.在乐曲中重复出现。(可能经过转调,“转调”的意思是主题序列中每个音符都被加上或减去了同一个整数值)

    3.重复出现的同一主题不能有公共部分。

 思路:求不可重叠最长重复子串,二分法或循环遍历求长度。


二分代码:

#include <iostream>
#include <stdio.h>
#include <algorithm>
#include <string.h>
using namespace std;
const int MAXN=20010;

int sa[MAXN];//SA数组,表示将S的n个后缀从小到大排序后把排好序的
             //的后缀的开头位置顺次放入SA中
int t1[MAXN],t2[MAXN],c[MAXN];//求SA数组需要的中间变量,不需要赋值
int rank[MAXN],height[MAXN];

void build_sa(int s[],int n,int m)
{
    int i,j,p,*x=t1,*y=t2;
    //第一轮基数排序,如果s的最大值很大,可改为快速排序
    for(i=0;i<m;i++)c[i]=0;
    for(i=0;i<n;i++)c[x[i]=s[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;
        //直接利用sa数组排序第二关键字
        for(i=n-j;i<n;i++)y[p++]=i;//后面的j个数第二关键字为空的最小
        for(i=0;i<n;i++)if(sa[i]>=j)y[p++]=sa[i]-j;
        //这样数组y保存的就是按照第二关键字排序的结果
        //基数排序第一关键字
        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];
        //根据sa和x数组计算新的x数组
        swap(x,y);
        p=1;x[sa[0]]=0;
        for(i=1;i<n;i++)
            x[sa[i]]=y[sa[i-1]]==y[sa[i]] && y[sa[i-1]+j]==y[sa[i]+j]?p-1:p++;
        if(p>=n)break;
        m=p;//下次基数排序的最大值
    }
}
void getHeight(int s[],int n)
{
    int i,j,k=0;
    for(i=0;i<=n;i++)rank[sa[i]]=i;
    for(i=0;i<n;i++)
    {
        if(k)k--;
        j=sa[rank[i]-1];
        while(s[i+k]==s[j+k])k++;
        height[rank[i]]=k;
    }
}
int s[MAXN];
bool check(int n,int k)//k为可能出现的子串长度
{
    int Max=sa[1],Min=sa[1];
    for(int i=2;i<=n;i++)
    {
        if(height[i]<k)Max=Min=sa[i];
        else
        {
            if(sa[i]<Min)Min=sa[i];
            if(sa[i]>Max)Max=sa[i];
            if(Max-Min>k)return true;
        }
    }
    return false;
}
int main()
{
    //freopen("in.txt","r",stdin);
    //freopen("out.txt","w",stdout);
    int n;
    while(scanf("%d",&n)==1 && n)
    {
        for(int i=0;i<n;i++)scanf("%d",&s[i]);
        for(int i=n-1;i>0;i--)s[i]=s[i]-s[i-1]+90;
        n--;//减少一个长度
        for(int i=0;i<n;i++)s[i]=s[i+1];
        s[n]=0;
        build_sa(s,n+1,200);
        getHeight(s,n);
        int ans=-1;
        int l=1,r=n/2;
        while(l<=r)
        {
            int mid=(l+r)/2;//mid为可能出现的子串长度
            if(check(n,mid))
            {
                ans=mid;
                l=mid+1;
            }
            else r=mid-1;
        }
        if(ans<4)printf("0\n");
        else printf("%d\n",ans+1);
    }
    return 0;
}


循环遍历代码:

//#pragma comment(linker, "/STACK:10240000000,10240000000")
#include<iostream>
#include<stdio.h>
#include<math.h>
#include <string>
#include<string.h>
#include<map>
#include<queue>
#include<set>
#include<utility>
#include<vector>
#include<algorithm>
#include<stdlib.h>
using namespace std;
#define eps 1e-8
#define pii pair<int,int>
#define inf 0x3f3f3f3f
#define rd(x) scanf("%d",&x)
#define rd2(x,y) scanf("%d%d",&x,&y)
#define rd3(x,y,z) scanf("%d%d%d",&x,&y,&z)
#define mo(x) memset(x,0,sizeof(x))
#define ll   long long int
#define ma(x,y) (x)>(y)?(x):(y)
#define mi(x,y) (x)<(y)?(x):(y)
#define mod 20071027
#define maxn 20010
#define maxm 10000001
int t1[maxn],t2[maxn],c[maxn];
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 rankk[],int height[],int n,int m){
    n++;
    int i,j,p,*x=t1,*y=t2;
    for(int i=0;i<m;i++) c[i]=0;
    for(int i=0;i<n;i++) c[x[i]=str[i]]++;
    for(int i=1;i<m;i++) c[i]+=c[i-1];
    for(int i=n-1;i>=0;i--) sa[--c[x[i]]]=i;
    for(int 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++) rankk[sa[i]]=i;
    for(i=0;i<n;i++){
        if(k) k--;
        j=sa[rankk[i]-1];
        while(str[i+k]==str[j+k]) k++;
        height[rankk[i]]=k;
    }
}
int Rank[maxn],height[maxn];
int str[maxn];
int r[maxn],sa[maxn];
int x,n;
int main()
{
    while(scanf("%d",&n)&&n){
        for(int i=0;i<n;i++){
            rd(str[i]);
            str[i-1]=str[i]-str[i-1]+100;
            //str[i]=char(x);

        }
        n--;
        str[n]=0;
        da(str,sa,Rank,height,n,200);
        int res=0;
        for(int i=1;i<n;i++)
        {
            int len=n;
            for(int j=i+1;j<=n;j++)
            {
                len=mi(len,height[j]);
                if(len<4||len<=res) break;
                int kk=mi(abs(sa[i]-sa[j]),len);
                res=ma(res,kk);
            }
        }
        printf("%d\n",res==0?0:res+1);
    }
    return 0;
}


你可能感兴趣的:(poj 1743 Musical Theme(后缀数组))