游程编码的基本原理是:用一个符号值或串长代替具有相同值的连续符号(连续符号构成了一段连续的“行程”。游程编码因此而得名),使符号长度少于原始数据的长度。只在各行或者各列数据的代码发生变化时,一次记录该代码及相同代码重复的个数,从而实现数据的压缩。
游程编码(Run Length Encoding , RLE)
例如:5555557777733322221111111
游程编码为:(5,6)(7,5)(3,3)(2,4)(1,7)
解题思路很好:
用value[i] count[i] 分别表示 第i段的数值 和 出现次数;
num[p] left[p] right[p]分别表示位置p所在段的编号和左右端点的位置。
每次查询(left,right)的结果为以下三个部分的最大值:从left到left所在段结束处的元素个数、从right所在段开始到right处的元素个数、中间第num[left]+1段到第num[right]-1段的count的最大值。
特殊情况:如果left和right在同一段中,答案是R-L+1。
解决方法如下:
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
const int maxn = 100001;
int n,q;
int d[maxn][35];
int a[maxn];
int value[maxn],count_[maxn];
int num[maxn],left[maxn],right[maxn];
void RMQ_int(){
for(int i=0;i<n;i++) d[i][0]=count_[i];
for(int j=1; (1<<j)<=n; j++)
for(int i=0; i+(1<<j)-1<n; i++)
d[i][j]=max(d[i][j-1],d[i+(1<<j-1)][j-1]);
}
int RMQ(int L,int R){
int k=0;
while((1<<(k+1))<=(R-L+1)) k++;
return max(d[L][k],d[R-(1<<k)+1][k]);
}
int main()
{
while(scanf("%d",&n)!=EOF){
if(n==0) break;
scanf("%d",&q);
memset(count_,0,sizeof(count_));
memset(num,0,sizeof(num));
memset(left,0,sizeof(left));
memset(right,0,sizeof(right));
memset(d,0,sizeof(d));
for(int i=0;i<n;i++)
scanf("%d",&a[i]);
int temp = a[0];
int t=0;
value[t]=temp;count_[t]++;
num[0]=t;left[0]=0;
for(int i=1;i<n;i++){
if(a[i]==temp){
count_[t]++;
num[i]=t;left[i]=left[i-1];
}
else{
for(int j=left[i-1];j<i;j++){
right[j]=i-1;
}
temp = a[i];
t++;
value[t] = temp;count_[t]++;
num[i]=t;left[i]=i;
}
}
t++;
for(int i=left[n-1];i<n;i++){
right[i]=n-1;
}
RMQ_int();
int left_,right_;
int ans=0;
for(int i=0;i<q;i++){
scanf("%d%d",&left_,&right_);
left_--;right_--;
if(num[left_]==num[right_]) ans=right_-left_+1;
else{
ans=max(right[left_]-left_+1,right_-left[right_]+1);
if(num[left_]+1 > num[right_]-1) ans=max(ans,0);
else{
ans=max(ans,RMQ(num[left_]+1,num[right_]-1));
}
}
printf("%d\n",ans);
}
}
return 0;
}