1. 程式人生 > >POJ1743(字尾陣列)

POJ1743(字尾陣列)

題目

題意:有N(1 <= N <=20000)個音符的序列來表示一首樂曲,每個音符都是1..88範圍內的整數,現在要找一個重複的主題。“主題”是整個音符序列的一個子串,它需要滿足如下條件:
1.長度至少為5個音符。
2.在樂曲中重複出現。(可能經過轉調,“轉調”的意思是主題序列中每個音符都被加上或減去了同一個整數值)
3.重複出現的同一主題不能有公共部分。

即給出一串字元,求不重合的最長重複子串,並且長度大於要求的k值.

思路:將height值分組,然後記錄在二分答案時滿足height值>=p的sa[i]的最大最小值,然後要是最大值減去最小值會>=p,這就說明兩個子串的lcp值>=p並且它們的座標也相差>=p。
另外避免“轉調”的影響,通過求相鄰序列的差值解決。

#include<iostream>
#include<stdio.h>
#include<string.h>
using namespace std;
#define maxx 20010
int wsf[maxx],wa[maxx],wv[maxx],wb[maxx],s[maxx];
int height[maxx],rank[maxx],sa[maxx];
int cmp(int *r,int a,int b,int k)
{
    return r[a]==r[b]&&r[a+k]==r[b+k];
}
void getsa(int
*r,int *sa,int n,int m) { int i,j,p,*x=wa,*y=wb,*t; for(i=0;i<m;i++) wsf[i]=0; for(i=0;i<n;i++) wsf[x[i]=r[i]]++; for(i=1;i<m;i++) wsf[i]+=wsf[i-1]; for(i=n-1;i>=0;i--) sa[--wsf[x[i]]]=i; j=1; p=1; for(;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++) wsf[i]=0; for(i=0;i<n;i++) wsf[wv[i]]++; for(i=1;i<m;i++) wsf[i]+=wsf[i-1]; for(i=n-1;i>=0;i--) sa[--wsf[wv[i]]]=y[i]; t=x; x=y; y=t; x[sa[0]]=0; for(i=1,p=1;i<n;i++) x[sa[i]]=cmp(y,sa[i-1],sa[i],j)?p-1:p++; } } void getheight(int *r,int n) { int i,j,k=0; for(i=1;i<=n;i++) rank[sa[i]]=i; for(i=0;i<n;i++) { if(k) k--; else k=0; j=sa[rank[i]-1]; while(r[i+k]==r[j+k]) k++; height[rank[i]]=k; } } int deal(int n,int p) { int minx=sa[0],maxx1=sa[0]; for(int i=0;i<=n;i++) { if(height[i]>=p) { if(minx>sa[i]) minx=sa[i]; if(maxx1<sa[i]) maxx1=sa[i]; if(maxx1-minx>p) return 1; } else minx=maxx1=sa[i]; } return 0; } int main() { int n; while(scanf("%d",&n)>0&&n) { for(int i=0;i<n;i++) scanf("%d",&s[i]); for(int i=0;i<n-1;i++) { s[i]=s[i+1]-s[i]+90; //這裡要注意,題目相當於把資料進行平移了,避免了“變調”的影響 } n--; s[n]=0; getsa(s,sa,n+1,200); getheight(s,n); int left=0,right=n,mid,coun=0; while(left<=right) //二分處理 { mid=(left+right)/2; if(deal(n,mid)) { if(coun<mid) coun=mid; left=mid+1; } else right=mid-1; } if(coun<4) coun=0; //大於5個字元的情況下,合適 else coun++; printf("%d\n",coun); } return 0; }