题意:
给你N个数,他们之间的升降调可以定义为相邻两数之差,但旋律可以整体升阶或降阶,问最少五个数长的相同旋律。
心得:
这题可以说是很毒瘤了,WA了若干发终于A了,期间竟然还有RE。
先作差分数组,可以看出长度小于10的显然不够分剪枝减掉。
然后,二分枚举答案,L为0,R为N/2。
这题即求 不重复的最长公共子串,考虑到2 4 6 8 10作出差分数组为4个2,即最终答案大于等于4即可。
如果是重复的最长公共子串,只需输出height的最大值即可。
毕竟height的sa不相邻前缀最大值是相邻的最小值,则结果一定是sa相邻的。
细节部分:
①作差可以作出负的,考虑到1-88的旋律,对差分数组整体+90,这样元素就分布到0-200之间。
②差分数组的实际长度,加上后补0的长度,最后还是N,预处理sa数组与high数组要注意。
③C数组,基数排序临时存排名的数组别开小了,开200直接RE,我也不知道为啥。
④对于连续的high数组,只要让最末SA排名对应前缀起始位置tall和最初SA排名对应前缀起始位置low之间,差出一个二分的mid答案即可,即若s[3]-s[5]相同,s[6]-s[8]相同,且二者公共前缀长度为3,只需让tall-low≥(5-3+1)即可,则二者没有重叠部分。
对于aaaabb,aaaabc,aaaacc这种连续相同前缀,只需让最大-最小≥len即可。
if(high[i]<mid)low=sa[i],tall=sa[i];
else
{
low=min(low,sa[i]);
tall=max(tall,sa[i]);
if(tall-low>=mid){flag=1;break;}
}
⑤二分的操作,除了while(r-l>1)if(judge(mid))l=mid;else r=mid; //l为最终答案以外,
又见到一个while(l<=r)if(flag)l=mid+1;else r=mid-1;//r为最终答案的写法
//这个可以理解成,比l小的都是可行解,比r大的都是不行解;
反过来说,小于等于r的都是可行解,即r是最大的可行解。
或者说,l-1也是最大的可行解,毕竟脱离循环是r=l-1;
思路来源:https://blog.csdn.net/crescent__moon/article/details/23201229
完整代码:
#include <iostream>
#include <algorithm>
#include <string>
#include <cstring>
#include <cstdio>
#include <cmath>
#include <set>
#include <map>
#include <vector>
#include <stack>
#include <queue>
#include <bitset>
const int INF=0x3f3f3f3f;
const int mod=1e9+7;
const double eps=1e-7;
typedef long long ll;
#define vi vector<int>
#define si set<int>
#define pii pair<int,int>
#define pi acos(-1.0)
#define pb push_back
#define mp make_pair
#define lowbit(x) (x&(-x))
#define sci(x) scanf("%d",&(x))
#define scll(x) scanf("%lld",&(x))
#define sclf(x) scanf("%lf",&(x))
#define pri(x) printf("%d",(x))
#define rep(i,j,k) for(int i=j;i<=k;++i)
#define per(i,j,k) for(int i=j;i>=k;--i)
#define mem(a,b) memset(a,b,sizeof(a))
using namespace std;
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
const int N = 20010;
int s[N],v[N];
int sa[N], t[N], t2[N], c[N], n;
int Rank[N], high[N],ans;
void build(int m)
{
int i, *x = t, *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(int k = 1, p; k <= n; k<<=1, m=p) {
p = 0;
for(i = n-k; i < n; i++) y[p++] = i;
for(i = 0; i < n; i++) if(sa[i] >= k) y[p++] = sa[i] - k;
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]] = (y[sa[i-1]] == y[sa[i]] && y[sa[i-1]+k] == y[sa[i]+k]) ? p-1 : p++;
}
if(p >= n) break;
}
}
/*
void get_high()
{
int j,k=0;
for(int i=0;i<n;++i)Rank[sa[i]]=i;
for(int i=0;i<n-1;high[Rank[i++]]=k)
for(k?k--:0,j=sa[Rank[i]-1];s[i+k]==s[j+k];k++);
for(int i=0;i<n;++i)printf("%d:%d %d\n",i,sa[i],high[i]);
}*/
void get_height()//求height函数
{
int i,j,k=0;
for(i=1;i<n;i++)Rank[sa[i]]=i;//求rank函数
for(i=0;i<n;high[Rank[i++]]=k)
for(k?k--:0,j=sa[Rank[i]-1];s[i+k]==s[j+k];k++);
//for(int i=0;i<n;++i)printf("%d:%d %d\n",i,sa[i],high[i]);
}
int solve(int t)
{
int l=0,r=t/2,mid;//二分最终答案长度
bool flag=0;
while(l<=r)
{
mid=(l+r)>>1;
int low=sa[1],tall=sa[1];
flag=0;
rep(i,2,n-1)
{
if(high[i]<mid)low=sa[i],tall=sa[i];
else
{
low=min(low,sa[i]);
tall=max(tall,sa[i]);
if(tall-low>=mid){flag=1;break;}
}
}
if(flag)l=mid+1;
else r=mid-1;
}
return r>=4?r+1:0;
}
int main()
{
while(scanf("%d",&n),n)
{
for(int i=0;i<n;++i)scanf("%d",&v[i]);
if(n<10){puts("0");continue;}
for(int i=0;i<n-1;++i)s[i]=v[i+1]-v[i]+90;//最小负值-87 加90为正
s[n-1]=0;
int maxi = 0;
for(int i = 0; i < n; i++)maxi = maxi > s[i] ? maxi : s[i];
build(maxi+1);
get_height();
printf("%d\n",solve(n));
}
return 0;
}
自己真是啥也不会啊,好好补题吧QAQ