poj 1743 Musical Theme(最長重復子串 後綴數組)
阿新 • • 發佈:2018-06-13
這樣的 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(最長重復子串 後綴數組)