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


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

    分析:首先这个重复子串的长度至少为5,因此在最后处理结果的时候要注意。"转调"我们可以利用差分来处理,如果有两段的差值都相同的话,那么就存在两段的主题是相同的。然后我们可以利用后缀数组的高度数组求解这两个重复子串的最大长度,我们可以二分最大长度,然后检测这个最大长度是否存在,对于高度数组来说,高度数组表示以字典序排序的后缀字符串的最长公共前缀,我们只需要比较连续的一组中最小的字符串开头位置和最大的字符串开头位置,然后求解这两个差值是否大于我们的二分长度即可。

    #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
    ", l + 1);
    		else
    		{
    			puts("0");
    		}
    	}
    	
    	return 0;
    }
    
  • 相关阅读:
    Nancy学习
    微信公众号开发开发问题记-code been used
    C#——委托、Lambda表达式、闭包和内存泄漏
    【协作式原创】查漏补缺之Go的slice基础和几个难点
    【协作式原创】自己动手写docker之run代码解析
    【算法】剑指第二版3.数组中重复数字
    剑指offer第二版速查表
    【协作式原创】查漏补缺之乐观锁与悲观锁TODO
    【协作式原创】查漏补缺之Go并发问题(单核多核)
    【协作式原创】查漏补缺之Golang中mutex源码实现(预备知识)
  • 原文地址:https://www.cnblogs.com/pixel-Teee/p/13359955.html
Copyright © 2020-2023  润新知