首页 > 编程语言 > 详细

POJ-1743 Musial Theme(后缀数组 + 二分)(男人八题)

时间:2020-07-22 14:27:30      阅读:65      评论:0      收藏:0      [点我收藏+]

题意:有N(1 <= N <= 20000)个音符的序列来表示一首乐曲,每个音符都是1...88范围内的整数,现在要找一个重复的子串,它需要满足如下条件:1.长度至少为5个音符。2.在乐曲中重复出现(就是出现过至少两次)。(可能经过转调,"转调"的意思是主题序列中每个音符都被加上或者减去了同一个整数值)3.重复出现的同一主题不能有公共部分。

#include <iostream>
#include <cstdio>
#include <cstring>
#include <string>
#include <vector>
#include <algorithm>

using namespace std;
const int N = 20005;
const int inf = 0x3f3f3f3f;
int a[N];
int p[N];
int n, k;
int rk[N], tmp[N];
int sa[N], lcp[N];

bool compare_sa(int i, int j)
{
	if (rk[i] != rk[j]) return rk[i] < rk[j];
	else
	{
		int ri = i + k <= n ? rk[i + k] : -1;
		int rj = j + k <= n ? rk[j + k] : -1;
		return ri < rj;
	}
}

void construct_sa(int a[], int sa[])
{
	for (int i = 0; i <= n; ++i)
	{
		sa[i] = i;
		rk[i] = i < n ? a[i] : -1;
	}

	for (k = 1; k <= n; k *= 2)
	{
		sort(sa, sa + n + 1, compare_sa);
		tmp[sa[0]] = 0;
		for (int i = 1; i <= n; ++i)
		{
			tmp[sa[i]] = tmp[sa[i - 1]] + (compare_sa(sa[i - 1], sa[i]) ? 1 : 0);
		}
		for (int i = 0; i <= n; ++i)
		{
			rk[i] = tmp[i];
		}
	}
}

void construct_lcp(int a[], int sa[], int lcp[])
{
	for (int i = 0; i <= n; ++i) rk[sa[i]] = i;

	int h = 0;
	lcp[0] = 0;
	for (int i = 0; i < n; ++i)
	{
		int j = sa[rk[i] - 1];
		if (h > 0) --h;
		for (; j + h < n && i + h < n; ++h)
		{
			if (a[j + h] != a[i + h]) break;
		}
		lcp[rk[i] - 1] = h;
	}	
}

bool check(int mid)
{
	int mx = -inf, mn = inf;
	for (int i = 0; i <= n; ++i)
	{
		if (lcp[i] >= mid)
		{
			mx = max(mx, max(sa[i], sa[i + 1]));
			mn = min(mn, min(sa[i], sa[i + 1]));
			if (mx - mn >= mid) return true;
		}
		else
		{
			mx = -inf;
			mn = inf;
		}
	}
	return false;
}

void init()
{
	memset(sa, 0, sizeof sa);
	memset(lcp, 0, sizeof lcp);
	memset(rk, 0, sizeof rk);
	memset(tmp, 0, sizeof tmp);
	memset(p, 0, sizeof p);
}

int main()
{	

	while (scanf("%d", &n) != EOF, n)
	{
		init();
		for (int i = 0; i < n; ++i) scanf("%d", &p[i]);

		
		for (int i = 0; i < n - 1; ++i) a[i] = p[i + 1] - p[i];
		a[n] = 0;

		construct_sa(a, sa);
		construct_lcp(a, sa, lcp);

		int l = 0, r = n >> 1;
		while (l < r)
		{
			int mid = l + r + 1 >> 1;
			if (check(mid)) l = mid;
			else r = mid - 1;
		}
		if(l >= 4)
			printf("%d\n", l + 1);
		else
		{
			puts("0");
		}
	}
	
	return 0;
}

POJ-1743 Musial Theme(后缀数组 + 二分)(男人八题)

原文:https://www.cnblogs.com/pixel-Teee/p/13359955.html

(0)
(0)
   
举报
评论 一句话评论(0
关于我们 - 联系我们 - 留言反馈 - 联系我们:wmxa8@hotmail.com
© 2014 bubuko.com 版权所有
打开技术之扣,分享程序人生!