1. 程式人生 > >poj 1743 Musical Theme(最長重復子串 後綴數組)

poj 1743 Musical Theme(最長重復子串 後綴數組)

這樣的 ons 是否 i++ eight pri space scan urn

poj 1743 Musical Theme(最長重復子串 後綴數組)

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

首先把序列差分一下,那麽現在,問題就轉換成了:給定一個字符串,求最長重復子串,這兩個子串不能重疊。

先來看看最長可重疊重復子串吧。首先,一對極長重復子串必定可以表示為兩個後綴的LCP。同時,任意兩個後綴的LCP也必定對應一對極長重復子串。它們是滿射的關系。因此最長可重疊重復子串的長度相當於任意一對後綴的lcp中最長的,顯然就是最大的height。

那這個做法如何推廣到最長不可重疊後綴呢?我們可以發現,只有當兩個後綴的距離大於它們的lcp時,才能被納入計算。在這種情況下,我們不能保證height中的最大值一定是答案,有可能最優值的兩個後綴之間隔了多個後綴。做法是二分子串長度L,將後綴數組分成若幹組,每個組內的height都大於L。記錄組內位置最小的,位置最大的串,看看它們之間的距離是否大於子串長度。如果是這樣的,那麽mid=l+1,否則mid=r。

#include <cstdio> 
#include <cstring>
#include <algorithm>
using namespace std;

const
int maxn=4e4+5; int n, m, a[maxn]; int *x, *y, *t, wa[maxn], wb[maxn], ws[maxn], wv[maxn], sa[maxn], ht[maxn]; int cmp(int *r, int a, int b, int l){ return r[a]==r[b]&&r[a+l]==r[b+l]; } void da(int *r){ x=wa; y=wb; m=maxn; for (int i=0; i<m; ++i) ws[i]=0; for (int i=0; i<n; ++i) ++ws[x[i]=r[i]]; for
(int i=1; i<m; ++i) ws[i]+=ws[i-1]; for (int i=0; i<n; ++i) sa[--ws[r[i]]]=i; //sa數組必須排好序 int i, j, p=0; for (j=1; j<n&&p<n; j<<=1, m=p+1){ //p代表當前倍增情況下有多少不同的後綴 m應當變成p+1 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]]; //wv:第二關鍵詞中排i的數,在第一關鍵詞中排第幾 for (i=0; i<m; ++i) ws[i]=0; for (i=0; i<n; ++i) ++ws[x[i]]; //ws:第一關鍵詞中排名為i的數,總排名的範圍是多少 for (i=1; i<m; ++i) ws[i]+=ws[i-1]; for (i=n-1; i>=0; --i) sa[--ws[wv[i]]]=y[i]; t=x; x=y; y=t; x[sa[0]]=1; for (p=1, i=1; i<n; ++i) //rank必須從1開始以區分空串 x[sa[i]]=cmp(y, sa[i-1], sa[i], j)?p:++p; } memset(ht, 0, sizeof(ht)); for (i=0; i<n; ++i) --x[i]; p=0; for (i=0; i<n; ht[x[i++]]=p){ //枚舉原串中1到n的所有後綴 if (!x[i]) continue; for (p?p--:0, j=sa[x[i]-1]; r[i+p]==r[j+p]&&i+p<n; ++p); //p表示h[i] } return; } int main(){ while (~scanf("%d", &n)&&n){ for (int i=0; i<n; ++i) scanf("%d", &a[i]); for (int i=0; i<n; ++i) a[i]=a[i+1]-a[i]+88; --n; da(a); int l=0, r=n, k, flag; while (l<r){ k=(l+r)>>1; flag=0; int minm=sa[0], maxm=sa[0]; for (int i=1; i<=n; ++i){ if (ht[i]<k){ if (maxm-minm>k) flag=1; minm=sa[i]; maxm=sa[i]; continue; } minm=min(minm, sa[i]); maxm=max(maxm, sa[i]); } if (flag) l=k+1; else r=k; } printf("%d\n", l<5?0:l); } return 0; }

poj 1743 Musical Theme(最長重復子串 後綴數組)