您的位置:首页 > 其它

POJ - 1743 Musical Theme (后缀数组模板)

2017-12-03 20:38 295 查看
Musical Theme

Description

A musical melody is represented as a sequence of N (1<=N<=20000)notes that are integers in the range 1..88, each representing a key on the piano. It is unfortunate but true that this representation of melodies ignores the notion of musical timing; but, this
programming task is about notes and not timings. 

Many composers structure their music around a repeating &qout;theme&qout;, which, being a subsequence of an entire melody, is a sequence of integers in our representation. A subsequence of a melody is a theme if it: 
is at least five notes long 

appears (potentially transposed -- see below) again somewhere else in the piece of music 

is disjoint from (i.e., non-overlapping with) at least one of its other appearance(s)

Transposed means that a constant positive or negative value is added to every note value in the theme subsequence. 

Given a melody, compute the length (number of notes) of the longest theme. 

One second time limit for this problem's solutions! 

Input

The input contains several test cases. The first line of each test case contains the integer N. The following n integers represent the sequence of notes. 

The last test case is followed by one zero. 

Output

For each test case, the output file should contain a single line with a single integer that represents the length of the longest theme. If there are no themes, output 0.
Sample Input
30
25 27 30 34 39 45 52 60 69 79 69 60 52 45 39 34 30 26 22 18
82 78 74 70 66 67 64 60 65 80
0

Sample Output
5

Hint

Use scanf instead of cin to reduce the read time.
Source

LouTiancheng@POJ

题意:求不重叠的最长公共子串长度

解题思路:后缀数组,这里用来保存后缀数组模板!后缀数组的学习推荐论文  所有的模板都出自这篇论文  后缀数组——处理字符串的有力工具

解题时,主要用到三个数组,说明如下:

SA数组,即后缀数组,SA[i]记录的是,所有后缀按字典序排好序后,排第i的后缀的第一个字符的下标为SA[i]。

注意下标从0开始!即SA[0]代表字典序最小的那个后缀的第一个字符的下标!

rk数组,即rank排名数组,rk[i]记录的是,所有后缀按字典序排好序后,第一个字符的下标为i的后缀的排名为rk[i]。与SA为互逆关系.

下标从0开始!

height数组,height[i]记录的是SA[i]与SA[i-1]所代表的两个后缀的最长公共前缀的长度。

下标从1开始!

注意!字符串的下标也是从0开始。即第一个字符的下标为0.

为了统一代码和编码方便,我们在计算时,会在字符串末尾补一个0,即把字符串结束符也算进去,这样计算出来的结果更统一!SA[0]永远等于字符串长度。height[1]永远等于0!详见代码例子!

注意由于加了一个0,现在SA[0]~SA
都是有意义的,而不是SA[N-1],rk,height同理。

关于这道题,因为要不重叠,所以不能直接使用height数组,我们要二分长度,然后用height数组判断即可。

#include <iostream>
#include <deque>
#include <stdio.h>
#include <map>
#include <string>
#include <algorithm>
#include <vector>
#include <math.h>
#include <stack>
#include <queue>
#include <set>
using namespace std;

typedef long long int ll;

const int MAXN = 100000; //用DC3要开三倍大小

int wa[MAXN], wb[MAXN], wv[MAXN], JS[MAXN]; //计算SA用的辅助数组

int rk[MAXN], height[MAXN], SA[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, int *SA, int n, int m)
{
int i, j, p, *x = wa, *y = wb, *t;
for (i = 0; i < m; i++)
JS[i] = 0;
for (i = 0; i < n; i++)
JS[x[i] = r[i]]++;
for (i = 1; i < m; i++)
JS[i] += JS[i - 1];
for (i = n - 1; i >= 0; i--)
SA[--JS[x[i]]] = i;
for (j = 1, p = 1; 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++)
JS[i] = 0;
for (i = 0; i < n; i++)
JS[wv[i]]++;
for (i = 1; i < m; i++)
JS[i] += JS[i - 1];
for (i = n - 1; i >= 0; i--)
SA[--JS[wv[i]]] = y[i];
for (t = x, x = y, y = t, p = 1, x[SA[0]] = 0, i = 1; i < n; i++)
x[SA[i]] = cmp(y, SA[i - 1], SA[i], j) ? p - 1 : p++;
}
return;
}
/*******************/

/***后缀数组DC3解法***/
#define F(x) ((x) / 3 + ((x) % 3 == 1 ? 0 : tb))
#define G(x) ((x) < tb ? (x)*3 + 1 : ((x)-tb) * 3 + 2)
int c0(int *r, int a, int b)
{
return r[a] == r[b] && r[a + 1] == r[b + 1] && r[a + 2] == r[b + 2];
}
int c12(int k, int *r, int a, int b)
{
if (k == 2)
return r[a] < r[b] || r[a] == r[b] && c12(1, r, a + 1, b + 1);
else
return r[a] < r[b] || r[a] == r[b] && wv[a + 1] < wv[b + 1];
}
void sort(int *r, int *a, int *b, int n, int m)
{
int i;
for (i = 0; i < n; i++)
wv[i] = r[a[i]];
for (i = 0; i < m; i++)
JS[i] = 0;
for (i = 0; i < n; i++)
JS[wv[i]]++;
for (i = 1; i < m; i++)
JS[i] += JS[i - 1];
for (i = n - 1; i >= 0; i--)
b[--JS[wv[i]]] = a[i];
return;
}
void DC3(int *r, int *SA, int n, int m)
{
int i, j, *rn = r + n, *SAn = SA + n, ta = 0, tb = (n + 1) / 3, tbc = 0, p;
r
= r[n + 1] = 0;
for (i = 0; i < n; i++)
if (i % 3 != 0)
wa[tbc++] = i;
sort(r + 2, wa, wb, tbc, m);
sort(r + 1, wb, wa, tbc, m);
sort(r, wa, wb, tbc, m);
for (p = 1, rn[F(wb[0])] = 0, i = 1; i < tbc; i++)
rn[F(wb[i])] = c0(r, wb[i - 1], wb[i]) ? p - 1 : p++;
if (p < tbc)
DC3(rn, SAn, tbc, p);
else
for (i = 0; i < tbc; i++)
SAn[rn[i]] = i;
for (i = 0; i < tbc; i++)
if (SAn[i] < tb)
wb[ta++] = SAn[i] * 3;
if (n % 3 == 1)
wb[ta++] = n - 1;
sort(r, wb, wa, ta, m);
for (i = 0; i < tbc; i++)
wv[wb[i] = G(SAn[i])] = i;
for (i = 0, j = 0, p = 0; i < ta && j < tbc; p++)
SA[p] = c12(wb[j] % 3, r, wa[i], wb[j]) ? wa[i++] : wb[j++];
for (; i < ta; p++)
SA[p] = wa[i++];
for (; j < tbc; p++)
SA[p] = wb[j++];
return;
}
/***********************/

//计算rank和height数组
void calheight(int *r, int *SA, int n)
{
//  memset(height,0,sizeof(height));
//  memset(rk,0,sizeof(rk));
int i, j, k = 0;
for (i = 1; i <= n; i++)
rk[SA[i]] = i;
for (i = 0; i < n; height[rk[i++]] = k)
for (k ? k-- : 0, j = SA[rk[i] - 1]; r[i + k] == r[j + k]; k++)
;
}

int N;         //字符串长度
int str[MAXN]; //主字符串
bool judge(int len)
{

int l = SA[1];
int r = SA[1];

for (int i = 2; i <= N; i++)
{
if (height[i] < len)
{
l = SA[i];
r = SA[i];
continue;
}
r = max(r, SA[i]);
l = min(l, SA[i]);
if (r - l > len)
return true;
}
return false;
}

int main()
{

while (~scanf("%d", &N))
{
if (N == 0)
break;

for (int i = 0; i < N; i++)
scanf("%d", &str[i]);
for (int i = 0; i < N - 1; i++)
str[i] = str[i + 1] - str[i] + 90;
str[--N] = 0; //必须要末尾补0!!!!!

DA(str, SA, N + 1, 200);//N是没有补0的大小,算SA时要把末尾0计算进去,所以要N+1
calheight(str, SA, N);//计算height时不用末尾0

int l = 0;
int r = N;
int m;
int ans = 0;
while (l < r)
{
m = (l + r) / 2;
if (judge(m))
{
ans = m;
l = m + 1;
}
else
r = m;
}
if (ans < 4)
printf("0\n");
else
printf("%d\n", ans + 1);
}

return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: