• POJ1743 Musical Theme-后缀数组


    Description

    题意:有 N(1 <= N <=20000) 个音符的序列来表示一首乐曲,每个音符都是 1..88 范围内的整数,现在要找一个重复的主题。“主题” 是整个音符序列的一个子串,它需要满足如下条件:

    1. 长度至少为 5 个音符。
    
    2. 在乐曲中重复出现。(可能经过转调,“转调” 的意思是主题序列中每个音符都被加上或减去了同一个整数值)
    
    3. 重复出现的同一主题不能有公共部分。
    

    Solution

    后缀数组经典应用, 求最长不重叠的重复子串

    此题是转调相同, 那么拿出差分数组来跑后缀数组。

    原数组不重叠, 差分数组两个子串要隔开(1)个字符

    然后二分长度(x), 两个字符串在一段 (h[i] >=x)的连续区间内, 且首字母位置相距(>x)

    最后的原数组重复子串的长度为 (ans + 1)

    Code

    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    #define rd read()
    using namespace std;
    
    const int N = 2e4 + 5, inf = 1e9;
    
    int tax[N], tmp[N], Rank[N], SA[N], rk[N], n, m, h[N], a[N]; 
    
    int read() {
    	int X = 0, p = 1; char c = getchar();
    	for (; c > '9' || c < '0'; c = getchar())
    		if (c == '-') p = -1;
    	for (; c >= '0' && c <= '9'; c = getchar())
    		X = X * 10 + c - '0';
    	return X * p;
    }
    
    void radix_sort() {
    	memset(tax, 0, sizeof(int) * (m + 1));
    	for (int i = 1; i <= n; ++i)
    		++tax[Rank[i]];
    	for (int i = 1; i <= m; ++i)
    		tax[i] += tax[i - 1];
    	for (int i = n; i; --i)
    		SA[tax[Rank[tmp[i]]]--] = tmp[i];
    }
    
    int cmp(int x, int y, int w) {
    	if (tmp[x] != tmp[y]) return 0;
    	if ((x + w > n ? -1 : tmp[x + w]) == (y + w > n ? -1 : tmp[y + w])) return 1;
    	return 0;
    }
    
    void get_SA() {
    	for (int i = 1; i <= n; ++i)
    		Rank[i] = a[i], tmp[i] = i;
    	m = 233;
    	radix_sort();
    	for (int num = 1, w = 1; num < n; w <<= 1, m = num) {
    		num = 0;
    		for (int i = n - w + 1; i <= n; ++i)
    			tmp[++num] = i;
    		for (int i = 1; i <= n; ++i) if (SA[i] > w)
    			tmp[++num] = SA[i] - w;
    		radix_sort();
    		for (int i = 1; i <= n; ++i)
    			tmp[i] = Rank[i];
    		num = Rank[SA[1]] = 1;
    		for (int i = 2; i <= n; ++i)
    			if (cmp(SA[i], SA[i - 1], w)) Rank[SA[i]] = num;
    		else Rank[SA[i]] = ++num;
    	}
    }
    
    void get_h() {
    	for (int i = 1; i <= n; ++i)
    		rk[SA[i]] = i;
    	for (int k = 0, i = 1; i <= n; ++i) {
    		if (k) k--;
    		int j = SA[rk[i] - 1];
    		while (a[k + i] == a[k + j]) k++;
    		h[rk[i]] = k;
    	}
    }
    
    void up(int &A, int B) {
    	if (A < B) A = B;
    }
    
    void down(int &A, int B) {
    	if (A > B) A = B;
    }
    
    int check(int x) {
    	for (int i = 2, Min = inf, Max = -inf; i <= n; ++i) {
    		if (h[i] >= x) {
    			down(Min, SA[i]); down(Min, SA[i - 1]);
    			up(Max, SA[i]); up(Max, SA[i - 1]);
    			if (Max - Min > x) return 1;
    		} else {
    			Min = inf; Max = -inf;
    		}
    	}
    	return 0;
    }
    
    int work() {
    	n = rd; 
    	if (!n) return 0;
    	for (int i = 1; i <= n; ++i)
    		a[i] = rd;
    	for (int i = 1; i < n; ++i)
    		a[i] = a[i + 1] - a[i] + 100;
    	n--;
    	get_SA();
    	get_h();
    	int l = 1, r = n, ans = 0;
    	for (; l <= r;) {
    		int mid = (l + r) >> 1;
    		if (check(mid)) ans = mid, l = mid + 1;
    		else r = mid - 1;
    	}
    	if (ans < 4) puts("0");
    	else printf("%d
    ", ans + 1); 
    	return 1;
    }
    
    int main()
    {
    	while (work());
    	return 0;
    }
    
  • 相关阅读:
    【Android】Android消息处理机制
    【Android】Sensor框架HAL层解读
    【Android】Sensor框架Framework层解读
    【流媒体】初识流媒体与流媒体技术
    【Git】Git与GitHub 入门
    【Delphi】基于状态机的串口通信
    【Delphi】SPComm注意事项
    【Android】事件输入系统-代码层次解读
    【Android】事件处理系统
    【Android】窗口机制分析与UI管理系统
  • 原文地址:https://www.cnblogs.com/cychester/p/10064162.html
Copyright © 2020-2023  润新知