poj1743 Musical Theme(后缀数组--不可重叠最长重复子串+二分)
2015-09-04 15:37
375 查看
题目链接:点击打开链接
题意描述:给出一首歌词,找出这首歌的最长主题?
1、主题长度不能小于5
2、在乐曲中重复出现。(可能经过转调,“转调”的意思是主题序列中每个音符都被加上或减去了同一个整数值)
3、主题之间至少有一个和其他的没有重叠
解题思路:后缀数组+二分 O(nlogn)
分析:
1、首先我们可以对长度进行二分,这样问题就转化为是否存在长度为l的不可重叠重复子串?
2、显然对于这个问题我们通过遍历height即可求的时间为O(n)
3、对于条件2我们可以转化原始数组,后一项减去前一项即可,这样数组长度变为n-1即可
代码:
题意描述:给出一首歌词,找出这首歌的最长主题?
1、主题长度不能小于5
2、在乐曲中重复出现。(可能经过转调,“转调”的意思是主题序列中每个音符都被加上或减去了同一个整数值)
3、主题之间至少有一个和其他的没有重叠
解题思路:后缀数组+二分 O(nlogn)
分析:
1、首先我们可以对长度进行二分,这样问题就转化为是否存在长度为l的不可重叠重复子串?
2、显然对于这个问题我们通过遍历height即可求的时间为O(n)
3、对于条件2我们可以转化原始数组,后一项减去前一项即可,这样数组长度变为n-1即可
代码:
#include <cstdio> #include <iostream> #define maxn 20010 using namespace std; int wa[maxn],wb[maxn],wv[maxn],ww[maxn]; int cmp(int* r,int a,int b,int l){ return r[a]==r[b]&&r[a+l]==r[b+l]; } void da(int* r,int *sa,int n,int m){ int i,j,p,*x=wa,*y=wb,*t; for(i=0;i<m;++i) ww[i]=0; for(i=0;i<n;++i) ww[x[i]=r[i]]++; for(i=1;i<m;++i) ww[i]+=ww[i-1]; for(i=n-1;i>=0;i--) sa[--ww[x[i]]]=i; for(j=1,p=1;p<n;j*=2,m=p){ for(p=0,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<n;++i) wv[i]=x[y[i]]; for(i=0;i<m;++i) ww[i]=0; for(i=0;i<n;++i) ww[wv[i]]++; for(i=1;i<m;i++) ww[i]+=ww[i-1]; for(i=n-1;i>=0;--i) sa[--ww[wv[i]]]=y[i]; for(t=x,x=y,y=t,p=1,x[sa[0]]=0,i=1;i<n;++i) x[sa[i]]=cmp(y,sa[i-1],sa[i],j)?p-1:p++; } return ; } int ranks[maxn],height[maxn]; void calheight(int* r,int* sa,int n){ int i,j,k=0; for(i=1;i<=n;++i) ranks[sa[i]]=i; for(i=0;i<n;height[ranks[i++]]=k) for(k?k--:0,j=sa[ranks[i]-1];r[i+k]==r[j+k];k++); return; } int sa[maxn]; int check(int n,int k){ int maxx=sa[1],minx=sa[1]; for(int i=2;i<=n;++i){ if(height[i]<k) maxx=minx=sa[i]; else{ if(sa[i]<minx) minx=sa[i]; if(sa[i]>maxx) maxx=sa[i]; if(maxx-minx>=k) return true; } } return false; } int str[maxn],n; int main(){ while(scanf("%d",&n)!=EOF&&n!=0){ for(int i=0;i<n;++i) scanf("%d",&str[i]); for(int i=n-1;i>0;--i) str[i]=str[i]-str[i-1]+88; for(int i=0;i<n-1;++i) str[i]=str[i+1]; str[n-1]=0; da(str,sa,n,176); calheight(str,sa,n-1); int l=4,r=(n-1)/2; int ans=0; while(l<=r){ int mid=(l+r)>>1; if(check(n-1,mid)){ l=mid+1; ans=max(mid,ans); } else r=mid-1; } if(ans==0) printf("0\n"); else printf("%d\n",ans+1); } }
相关文章推荐
- nyoj 38 布线问题【最小生成树】
- Android基础入门教程——6.3.1 数据存储与访问之——初见SQLite数据库
- 海量数据处理: Top K算法(问题) 小顶堆实现
- HDU 3879 Base Station(最大权闭合)
- php这是一个随机打印输出字符串的例子
- NotePad大小写转换
- iOS 在UILabel显示不同的字体和颜色
- UVa 714 Copying Books (最大值尽量小_二分+贪心)
- 2015
- Git学习笔记
- 剑指offer:从尾到头打印链表
- 认识自己
- Exception starting filter struts2|java.lang.ClassNotFoundException
- Eclipse中开启java的assert选项
- hdu4291A Short problem 矩阵快速幂
- 3D游戏与计算机图形学中的数学方法-四元数
- PWM设计
- 1060. Are They Equal (25)
- mysql嵌套关联查询
- 最长公共子序列