• POJ-1743 Musical Theme 后缀数组求不可重叠最长重复子串


    POJ-1743 Musical Theme 后缀数组求不可重叠最长重复子串

    题意

    给出一段乐曲,计算其中最长“主题”的长度

    主题需要满足

    • 长度至少为5个字符
    • 在乐曲中重复出现(可能经过转调)
    • 重复出现的同一主题不能有公共部分

    所谓“转调”,是指每个音符都被加上或者减去同个值。

    乐曲中的每个音符都是1到88的整数

    给出N,表示这段乐曲有N个音符,后给出整数

    [1leq nleq 20000 ]

    分析

    首先转调,可以转化为差分做,再把整个乐曲看成一个字符串,问题就变成了字符串中不可重叠的最长重复子串,这个问题在罗老师的经典论文中已经详细说明,这里说几个注意点

    • 需要特判1
    • 处理这类问题通常用int代替char
    • 用刘汝佳老师的模板,通常需要加入一个'$',若是int,则是0
    • 将问题转化为判定性问题,分组 是处理后缀数组问题的常用手段

    代码

    int s[maxn],p[maxn];
    int sa[maxn],t[maxn],t2[maxn],c[maxn],n;
    
    void build_sa(int m){
    	int *x = t,*y = t2;
    	for(int i = 0;i < m;i++) c[i] = 0;
    	for(int i = 0;i < n;i++) c[x[i] = s[i]]++;
    	for(int i = 1;i < m;i++) c[i] += c[i -1];
    	for(int i = n - 1;i >= 0;i--) sa[--c[x[i]]] = i;
    	for(int k = 1;k <= n;k <<= 1){
    		int p = 0;
    		for(int i = n - k;i < n;i++) y[p++] = i;
    		for(int i = 0;i < n;i++) if(sa[i] >= k) y[p++] = sa[i] - k;
    		for(int i = 0;i < m;i++) c[i] = 0;
    		for(int i = 0;i < n;i++) c[x[y[i]]]++;
    		for(int i = 0;i < m;i++) c[i] += c[i - 1];
    		for(int i = n - 1;i >= 0;i--) sa[--c[x[y[i]]]] = y[i];
    		swap(x,y);
    		p = 1;
    		x[sa[0]] = 0;
    		for(int i = 1;i < n;i++)
    			x[sa[i]] = y[sa[i - 1]] == y[sa[i]] && y[sa[i] + k] == y[sa[i - 1] + k] ? p - 1:p++;
    		if(p >= n) break;
    		m = p;
    	}
    }
    
    int rak[maxn],height[maxn];
    
    void getHeight(){
    	int k = 0;
    	for(int i = 0;i < n;i++) rak[sa[i]] = i;
    	for(int i = 0;i < n;i++){
    		if(k) k--;
    		int j = sa[rak[i] - 1];
    		while(s[i + k] == s[j + k]) k++;
    		height[rak[i]] = k;
    	}
    }
    
    bool check(int len){
    	int mx = sa[0],mi = sa[0];
    	for(int i = 1;i < n;i++){
    		if(height[i] >= len - 1) 
    			mx = max(sa[i],mx),mi = min(sa[i],mi);
    		else 
    			mx = mi = sa[i];
    		if(mx - mi >= len) return true;
    	}
    	return false;
    }
    
    int solve(){
    	int l = 0,r = n,ans = 0;
    	while(l <= r) {
    		int mid = l + r  >> 1;
    		if(check(mid)) l = mid + 1,ans = mid;
    		else r = mid - 1;
    	}
    	return ans;
    }
    
    int main(){
    	while(~scanf("%d",&n) && n){
    		for(int i = 0;i < n;i++) p[i] = readint();
    		if(n == 1) {
    			puts("0");
    			continue;
    		}
    		for(int i = 0;i < n - 1;i++) s[i] = p[i + 1] - p[i] + 100;
    		s[n - 1] = 0;
    		build_sa(200);
    		getHeight();
    		int ans = solve();
    		if(ans >= 5) printf("%d
    ",ans);
    		else puts("0");
    	}
    	
    }
    
  • 相关阅读:
    Ubuntu apt-get update 失败
    Ubuntu无法访问windows分区
    Python实现使用tkinter弹出输入框输入数字, 具有确定输入和清除功能
    如何更改监控器的默认计数器
    健壮的 Java 基准测试
    从虚拟机视角谈 Java 应用性能优化
    LoadRunner如何调用外部函数
    git安装与上传
    Loadrunner安装与破解【转】
    性能测试方法【转】
  • 原文地址:https://www.cnblogs.com/hznumqf/p/14057990.html
Copyright © 2020-2023  润新知