poj 1743 男人八题之后缀数组求最长不可重叠最长重复子串
| Time Limit: 1000MS | Memory Limit: 30000K | |
| Total Submissions: 14874 | Accepted: 5118 |
Description
Many composers structure their music around a repeating &qout;theme&qout;, which, being a subsequence of an entire melody, is a sequence of integers in our representation. A subsequence of a melody is a theme if it:
- is at least five notes long
- appears (potentially transposed -- see below) again somewhere else in the piece of music
- is disjoint from (i.e., non-overlapping with) at least one of its other appearance(s)
Transposed means that a constant positive or negative value is added to every note value in the theme subsequence.
Given a melody, compute the length (number of notes) of the longest theme.
One second time limit for this problem's solutions!
Input
The last test case is followed by one zero.
Output
Sample Input
30
25 27 30 34 39 45 52 60 69 79 69 60 52 45 39 34 30 26 22 18
82 78 74 70 66 67 64 60 65 80
0
Sample Output
5
Hint
Source
给定一个正整数N(N<=20000),然后是N个整数xi,(1<=xi<=88, 1<=i<=N)组成一个有序的整数序列;问这个序列中存在的最长一个符合条件的子序列长度是多少,符合的条件是
1、 子序列A长度至少为5;
2、 有另外一个子序列B,且A、B二者没有相交部分 不重叠;
3、 A,B的长度一样 AB相同或者 A的所有元素加上减去一个相同的数能变成B;
思路:
如果2个串满足上面条件 那么他们的每个元素a[i]-a[i-1] 即相邻的元素相减 得到的差值也应该是相同的 注意n个数的差中只要有n-1个数相同 (所以我代码中最后结果+1)那么这2个字串就是符合题意的
之后就是用后缀数组求不可重叠最长重复子串了
首先由二分答案的方法将问题变成判定性的:长度大于k的重复字串有没有?然后将height数组分组,每组内的后缀之间的height都要大于k,如果每组内的后缀之间的最长公共前缀有大于k的而且这两个后缀的sa[]之差大于k就说明存在长度至少为k的不重复子串。
#include<stdio.h>
#include<string.h>
#include<iostream>
#include<cstdio>
#include<cmath>
#include<vector>
#include<cstring>
using namespace std; const int nMax =1000012; int num[nMax];
int sa[nMax], rank[nMax], height[nMax];
int wa[nMax], wb[nMax], wv[nMax], wd[nMax];
int mmin(int a,int b)
{
if(a>b) return b;
return a;
}
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 n, int m){ // 倍增算法 r为待匹配数组 n为总长度 m为字符范围
int i, j, p, *x = wa, *y = wb, *t;
for(i = 0; i < m; i ++) wd[i] = 0;
for(i = 0; i < n; i ++) wd[x[i]=r[i]] ++;
for(i = 1; i < m; i ++) wd[i] += wd[i-1];
for(i = n-1; i >= 0; i --) sa[-- wd[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 ++) wd[i] = 0;
for(i = 0; i < n; i ++) wd[wv[i]] ++;
for(i = 1; i < m; i ++) wd[i] += wd[i-1];
for(i = n-1; i >= 0; i --) sa[-- wd[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 ++;
}
}
} void calHeight(int *r, int n){ // 求height数组。
int i, j, k = 0;
for(i = 1; i <= n; i ++) rank[sa[i]] = i; // 1->n
for(i = 0; i < n; i++){
for(k ? k -- : 0, j = sa[rank[i]-1]; r[i+k] == r[j+k]; k ++);
height[rank[i]] = k;
}
} int Log[nMax];
int best[20][nMax];//best[i][j] 表示从j开始的长度为2的i次方的一段元素的最小值
void initRMQ(int n)
{//初始化RMQ
int i,j;
for(i = 1; i <= n ; i ++) best[0][i] = height[i];
for(i = 1; i <= Log[n] ; i ++)
{
int limit = n - (1<<i) + 1;
for(j = 1; j <= limit ; j ++)
{
best[i][j] = mmin(best[i-1][j] , best[i-1][j+(1<<i>>1)]);
}
}
}
int lcp(int a,int b) {//询问a,b后缀的最长公共前缀
a = rank[a]; b = rank[b];
if(a > b) swap(a,b);
a ++;
int t = Log[b - a + 1];
return mmin(best[t][a] , best[t][b - (1<<t) + 1]);
} void get_log()
{
int i;
Log[0] = -1;
for(i=1;i<=nMax;i++)
{ // 求log2,这么强大的位运算。。
Log[i]=(i&(i-1))?Log[i-1]:Log[i-1] + 1 ;
}
}
char str[nMax];
int ans[nMax];
int n;
int a[nMax];
int solve(int x)
{
///注意通过判断sa[i]-sa[i-1]>=k决定不重复长度是否大于k是不行的 因为有可能有好几个重复的排在一起
///对于每个都不大于k 但是最后一个和第一个的距离是大于k的
///注意sa[i],sa[i-1]不一定哪个大那个小
int i,mx,mn;
mx=0,mn=nMax;
for(i=1;i<=n;i++)
{
if(height[i]>=x)
{
mx=max(mx,sa[i]);
mn=min(mn,sa[i]);
if(mx-mn>=x) return 1;
}
else
{
mx=mn=sa[i];
}
} return 0;
} int main()
{
int i,j;
get_log();
while(scanf("%d",&n)!=EOF)
{
if(!n) break;
for(i=0;i<n;i++) scanf("%d",&a[i]);
// n--;
for(i=1;i<n;i++)
{
num[i]=a[i]-a[i-1]+100;//加100防止出现负数
}
num[n]=0;
da(num,n+1,300);//这里要开大一点 300
calHeight(num,n);
initRMQ(n);
/*
for(i=0; i<n+1; i++) // rank[i] : suffix(i)排第几
printf("rank[%d] = %d\n",i,rank[i]);
printf("\n");
for(i=0; i<n+1; i++) // sa[i] : 排在第i个的是谁
printf("sa[%d] = %d\n",i,sa[i]);
*/
int left,right,mx=0,mid;
left=4;right=n/2+1;
while(left<=right)
{
mid=(left+right)/2;
if(solve(mid)&&mid>mx)
{
mx=mid;
left=mid+1;
}
else
{
right=mid-1;
}
}
if(mx==0) {printf("0\n");continue;}
printf("%d\n",mx+1);
}
return 0;
}
poj 1743 男人八题之后缀数组求最长不可重叠最长重复子串的更多相关文章
- POJ 1743 [USACO5.1] Musical Theme (后缀数组+二分)
洛谷P2743传送门 题目大意:给你一个序列,求其中最长的一对相似等长子串 一对合法的相似子串被定义为: 1.任意一个子串长度都大于等于5 2.不能有重叠部分 3.其中一个子串可以在全部+/-某个值后 ...
- poj 1737男人八题之一 orz ltc
这是楼教主的男人八题之一.很高兴我能做八分之一的男人了. 题目大意:求有n个顶点的连通图有多少个. 解法: 1. 用总数减去不联通的图(网上说可以,我觉得时间悬) 2. 用动态规划(数学递推) ...
- POJ 1743:Musical Theme(后缀数组+二分)
题目链接 题意 有N个音符的序列来表示一首乐曲,每个音符都是1到88范围内的整数,现在要找一个重复的主题."主题"是整个音符序列的一个子串,它需要满足如下条件: 长度至少为5个音符 ...
- poj 1741 楼教主男人八题之中的一个:树分治
http://poj.org/problem? id=1741 Description Give a tree with n vertices,each edge has a length(posit ...
- poj 1743 二分答案+后缀数组 求不重叠的最长重复子串
题意:给出一串序列,求最长的theme长度 (theme:完全重叠的子序列,如1 2 3和1 2 3 or 子序列中每个元素对应的差相等,如1 2 3和7 8 9) 要是没有差相等这个条件那就好办 ...
- POJ1742 Coins(男人八题之一)
前言 大名鼎鼎的男人八题,终于见识了... 题面 http://poj.org/problem?id=1742 分析 § 1 多重背包 这很显然是一个完全背包问题,考虑转移方程: DP[i][j]表示 ...
- Cogs 1714. [POJ1741][男人八题]树上的点对(点分治)
[POJ1741][男人八题]树上的点对 ★★★ 输入文件:poj1741_tree.in 输出文件:poj1741_tree.out 简单对比 时间限制:1 s 内存限制:256 MB [题目描述] ...
- POJ 3294 UVA 11107 Life Forms 后缀数组
相同的题目,输出格式有区别. 给定n个字符串,求最长的子串,使得它同时出现在一半以上的串中. 不熟悉后缀数组的童鞋建议先去看一看如何用后缀数组计算两个字符串的最长公共子串 Ural1517 这道题的思 ...
- 【POJ2774】Long Long Message(后缀数组求Height数组)
点此看题面 大致题意: 求两个字符串中最长公共子串的长度. 关于后缀数组 关于\(Height\)数组的概念以及如何用后缀数组求\(Height\)数组详见这篇博客:后缀数组入门(二)--Height ...
随机推荐
- 基于visual Studio2013解决C语言竞赛题之0613递归求积
题目
- Cocos2d-x CCTableView实现列表
在ios程序设计中,会大量使用到tableview视图(UITableView),那么在cocos2d-x中,如果需要类似的列表,该如何实现呢?在引擎中参照ios中的UITableView实现了一个叫 ...
- Activity生命周期方法的调用顺序project与測试日志
以下为測试activity的方法的运行顺序 project与測试资源地址 androidproject AndroidManifest.xml <? xml version="1. ...
- B4A的软件下载
http://pan.baidu.com/share/home?uk=909467506#category/type=0
- PHP $_SERVER
$_SERVER 是一个包括了诸如头信息(header).路径(path).以及脚本位置(script locations)等等信息的数组.这个数组中的项目由 Web server创建.不能保证每一个 ...
- 写一个函数int get(),这个函数运行一次可以从V[N]里随机取出一个数,而这个数必须是符合1/N平均分布的
题目:有一个函数int getNum(),每运行一次可以从一个数组V[N]里面取出一个数,N未知,当数取完的时候,函数返回NULL.现在要求写一个函数int get(),这个函数运行一次可以从V[N] ...
- 1030 - Image Is Everything
Your new company is building a robot that can hold small lightweight objects. The robot will have th ...
- EasyUI - According 分类列表
效果: html代码: <div id="aa" class="easyui-accordion" style="width: 300px; h ...
- Socket开发时,Available为0,实际还有数据的问题
这段时间处理Socket通讯,比如文件传输,通常代码如下: string filename = @"c:\abc.txt"; // 发送文件名字符串长度(测试代码,实际请传输字符串 ...
- cocos2d-x 精灵移动
在HelloWorldScene.h中声明 class HelloWorld : public cocos2d::CCLayer { public : ...... CCPoin ...