zoukankan      html  css  js  c++  java
  • POJ 1743:Musical Theme(后缀数组+二分)

    题目链接

    题意

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

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

    思路

    论文里面出现的题目。

    因为加上和减去同一个整数值,就可以作差,因为减出来可能是个负数,因此需要+88.

    二分答案,转化为是否存在两个长度为k的子串是相同的,且不重叠。

    将height数组根据k分组,使得每组的后缀之间的height值都大于等于k,有希望成为最长公共前缀不小于k的两个后缀在同一组。

    要使得两个串不重叠,只要判断同一组里面最大的sa值和最小的sa值的差大于等于k,这样就可以不重叠了。

    #include <cstdio>
    #include <cstring>
    #include <algorithm>
    #include <iostream>
    using namespace std;
    typedef long long LL;
    typedef pair<int, int> pii;
    const int INF = 0x3f3f3f3f;
    const int N = 2e5 + 11;
    int t1[N], t2[N], c[N], s[N], str[N], Hash[N], sa[N], rak[N], heigth[N], n;
    
    bool cmp(int *r, int a, int b, int l) {
    	return r[a] == r[b] && r[a+l] == r[b+l];
    }
    
    void DA(int s[], int n, int m) {
    	n++;
    	int i, j, p, *x = t1, *y = t2;
    	for(i = 0; i < m; i++) c[i] = 0;
    	for(i = 0; i < n; i++) c[x[i] = s[i]]++;
    	for(i = 1; i < m; i++) c[i] += c[i-1];
    	for(i = n - 1; i >= 0; i--) sa[--c[x[i]]] = i;
    	for(j = 1; j < n; j <<= 1) {
    		p = 0;
    		for(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 < m; i++) c[i] = 0;
    		for(i = 0; i < n; i++) c[x[y[i]]]++;
    		for(i = 1; i < m; i++) c[i] += c[i-1];
    		for(i = n - 1; i >= 0; i--) sa[--c[x[y[i]]]] = y[i];
    
    		swap(x, y);
    		p = 1; x[sa[0]] = 0;
    		for(i = 1; i < n; i++)
    			x[sa[i]] = cmp(y, sa[i-1], sa[i], j) ? p - 1 : p++;
    		if(p >= n) break;
    		m = p;
    	}
    	n--;
    	int k = 0;
    	for(i = 0; i <= n; i++) rak[sa[i]] = i;
    	for(i = 0; i < n; i++) {
    		if(k) k--;
    		j = sa[rak[i]-1];
    		while(s[i+k] == s[j+k]) k++;
    		heigth[rak[i]] = k;
    	}
    }
    
    bool check(int k) {
    	int mi = INF, ma = -INF;
        for(int i = 2; i <= n; i++) {
    		if(heigth[i] >= k) {
    			mi = min(mi, min(sa[i], sa[i-1]));
    			ma = max(ma, max(sa[i], sa[i-1]));
    			if(ma - mi >= k) return 1;
    		} else {
    			mi = INF, ma = -INF;
    		}
        } return 0;
    }
    
    int main() {
    	while(scanf("%d", &n), n) {
    		for(int i = 0; i < n; i++) scanf("%d", &s[i]);
    		for(int i = 0; i < n; i++) s[i] = s[i+1] - s[i] + 88;
    		int m = 177;
    		n--;
    		s[n] = 0;
            DA(s, n, m);
            int l = 0, r = n, ans = l;
            while(l <= r) {
    			int mid = (l + r) >> 1;
                if(check(mid)) l = mid + 1, ans = mid;
                else r = mid - 1;
            }
            if(ans < 4) puts("0");
            else printf("%d
    ", ans + 1);
    	}
    	return 0;
    }
    
  • 相关阅读:
    jquery常用操作@测试分享
    selenium 上传文件
    python 安装mysql驱动
    创建react项目
    入栈操作的合法性 【重复元素】
    git笔记
    python GUI实战项目——tkinter库的简单实例
    Excel更改单元格格式后无效
    Find the Difference
    Two Sum IV
  • 原文地址:https://www.cnblogs.com/fightfordream/p/7772736.html
Copyright © 2011-2022 走看看