POJ1743 Musical Theme(二分+后缀数组)
2016-02-18 20:44
302 查看
题目大概是给n个数组成的串,求是否有多个“相似”且不重叠的子串的长度大于等于5,两个子串相似当且仅当长度相等且每一位的数字差都相等。
这题是传说中楼教主男人八题之一,虽然已经是用后缀数组解决不可重叠最长重复子串的经典题了。。但其实没那么简单,题目数据不强,网上一些代码都是不正确的。
首先把问题转化成重复子串的问题:把原串每一位都与前一位相减。这样得出的新串如果有两个长度为n的子串相同,那么它们对应在原串的长度n+1的子串也就相似。
所以接下来要求的就是这个新串不可“重叠”最长重复子串——问题就在这儿,这不只是要求不可重叠,还要求两个子串要隔至少一个位置,因为如果两个子串靠在一起这样反应到原串那两个子串各自的首尾是重合的。
比如数据:9 1 1 1 1 1 1 1 1 1
隔至少一个位置其实只要原本的if(mx-mm>=k)改成if(mx-mm>k)就行了。
最后大概描述一下不可重叠最长重复子串的解法:
O(logn)二分枚举子串长度,判断解是否成立
O(n)判断长度是否成立:把互相之间LCP大于等于长度的分为一组,这通过个扫一遍height即可,因为后缀是有序的,相邻的后缀间的LCP必定的极大的;接下来就找到每个组里后缀sa值最大和最小的,如果差值大于(等于)k就成立,因为这样小下标的后缀沿着LCP下去走k步才不会盖到大下标的后缀。
另外,说一下二分枚举解,二分具体写法很多吧,也不知道正不正确。。我那样的写法我发现:
如果是求最小解,mid要取floor,即mid=(left+right)/2
如果是求最大解,mid要取ceil,即mid=(left+right+1)/2
看起来好像是这个样子的。。
这题是传说中楼教主男人八题之一,虽然已经是用后缀数组解决不可重叠最长重复子串的经典题了。。但其实没那么简单,题目数据不强,网上一些代码都是不正确的。
首先把问题转化成重复子串的问题:把原串每一位都与前一位相减。这样得出的新串如果有两个长度为n的子串相同,那么它们对应在原串的长度n+1的子串也就相似。
所以接下来要求的就是这个新串不可“重叠”最长重复子串——问题就在这儿,这不只是要求不可重叠,还要求两个子串要隔至少一个位置,因为如果两个子串靠在一起这样反应到原串那两个子串各自的首尾是重合的。
比如数据:9 1 1 1 1 1 1 1 1 1
隔至少一个位置其实只要原本的if(mx-mm>=k)改成if(mx-mm>k)就行了。
最后大概描述一下不可重叠最长重复子串的解法:
O(logn)二分枚举子串长度,判断解是否成立
O(n)判断长度是否成立:把互相之间LCP大于等于长度的分为一组,这通过个扫一遍height即可,因为后缀是有序的,相邻的后缀间的LCP必定的极大的;接下来就找到每个组里后缀sa值最大和最小的,如果差值大于(等于)k就成立,因为这样小下标的后缀沿着LCP下去走k步才不会盖到大下标的后缀。
另外,说一下二分枚举解,二分具体写法很多吧,也不知道正不正确。。我那样的写法我发现:
如果是求最小解,mid要取floor,即mid=(left+right)/2
如果是求最大解,mid要取ceil,即mid=(left+right+1)/2
看起来好像是这个样子的。。
#include<cstdio> #include<cstring> #include<algorithm> using namespace std; #define MAXN 22222 #define INF (1<<30) int wa[MAXN],wb[MAXN],wv[MAXN],ws[MAXN]; int cmp(int *r,int a,int b,int l){ return r[a]==r[b] && r[a+l]==r[b+l]; } int sa[MAXN],rank[MAXN],height[MAXN]; void SA(int *r,int n,int m){ int *x=wa,*y=wb; for(int i=0; i<m; ++i) ws[i]=0; for(int i=0; i<n; ++i) ++ws[x[i]=r[i]]; for(int i=1; i<m; ++i) ws[i]+=ws[i-1]; for(int i=n-1; i>=0; --i) sa[--ws[x[i]]]=i; int p=1; for(int j=1; p<n; j<<=1,m=p){ p=0; for(int i=n-j; i<n; ++i) y[p++]=i; for(int i=0; i<n; ++i) if(sa[i]>=j) y[p++]=sa[i]-j; for(int i=0; i<n; ++i) wv[i]=x[y[i]]; for(int i=0; i<m; ++i) ws[i]=0; for(int i=0; i<n; ++i) ++ws[wv[i]]; for(int i=1; i<m; ++i) ws[i]+=ws[i-1]; for(int i=n-1; i>=0; --i) sa[--ws[wv[i]]]=y[i]; swap(x,y); x[sa[0]]=0; p=1; for(int i=1; i<n; ++i) x[sa[i]]=cmp(y,sa[i-1],sa[i],j)?p-1:p++; } for(int i=1; i<n; ++i) rank[sa[i]]=i; int k=0; for(int i=0; i<n-1; height[rank[i++]]=k){ if(k) --k; for(int j=sa[rank[i]-1]; r[i+k]==r[j+k]; ++k); } } int n,a[MAXN],r[MAXN]; bool isok(int k){ bool flag=0; int mx=-INF,mm=INF; for(int i=2; i<=n; ++i){ if(height[i]>=k){ mm=min(mm,min(sa[i],sa[i-1])); mx=max(mx,max(sa[i],sa[i-1])); if(mx-mm>k) return 1; }else{ mx=-INF,mm=INF; } } return 0; } int main(){ while(~scanf("%d",&n) && n){ for(int i=0; i<n; ++i) scanf("%d",a+i); --n; for(int i=0; i<n; ++i) r[i]=a[i+1]-a[i]+88; r =0; SA(r,n+1,176); int l=0,r=n>>1; while(l<r){ int mid=l+r+1>>1; if(isok(mid)) l=mid; else r=mid-1; } if(l>=4) printf("%d\n",l+1); else printf("%d\n",0); } return 0; }
相关文章推荐
- supervisor
- C++ 多线程的数据保护机制
- 自定义actionSheet 字体和颜色
- Java集合:集合框架
- leetCode 328. Odd Even Linked List
- 【图像处理】OTSU二值化原理及代码实现
- HDU 1556 Color the ball
- (6) linux shell 命令 -- rmdir
- 键盘实现鼠标功能
- Collection 和 Collections的区别。
- 字符串匹配与KMP算法笔记
- 学习笔记------数据结构(C语言版)数组之行逻辑链接的顺序表
- windows客户端开发--使你的客户端运行时记住上次关闭的大小和位置
- android中src和background区别
- 指针和宏定义bug小记
- ZOJ_3203_LightBulb
- windows客户端开发--使你的客户端运行时记住上次关闭的大小和位置
- 凸包问题-蛮力法
- Linux笔记(4)——命令基本格式与文件处理命令
- 学习gtest笔记<1-VS2013环境搭建>