CSPCCF认证202305-3解压缩 解析(不需要算法)

最近准备参加csp认证,做了几套题,放假闲的无聊写一下解析。

一般csp认证的第三道大题都是小模拟,主要考验我们做题者的阅读能力(最重要的!!!)以及敲代码的熟练程度,基本不会涉及到算法,适合我这种不会算法的小弱鸡。

a7ab272dfbb04452ae9585b6d16115f5.png

 608b9dc1f0584d8ebb3bf6c7f8d1a1ba.png

75a2b584140c4dcfbbd9202916ab9b02.png 

 01b70aab3de24c7c8863af9292b37da5.png

 d2d101a788eb4ca2ae8b49f4a8e91937.png

 2ec9f1b44a33413db009fcb9bb0fd975.png

 b50c8bdbaf8a497fbca0a770cf6aeeea.png

 52e4b2a528054e7cb7872901fcc63239.png

 85909a76cfc448dbacacb1331b25e2a4.png

 fba367af6ecb45dda2ba843fc208cd08.png

 33ca6f5bc302466187a36f5e5deddce8.png

 本题不需要任何算法,纯纯的模拟

虽然说不需要算法基础,但我们得先了解题干中的一个基础概念——小端序。以十进制为例1324的小端序为4231,简单来说小端序就是正常顺序(大端序)倒过来。

由于本题给的输入数据都是1个字节(2位16进制数),但处理时都是按2进制数进行处理,所以我们需要先构建一个将16进制数转化为2进制的函数

int six[20],num=1;    // num表示已经处理到第几个字节
void change(int n){
	for(int i=0;i<=19;i++) six[i]=0;
	int x,y;
	if(a[2*n-1]>='0'&&a[2*n-1]<='9') x=a[2*n-1]-'0';
	else x=a[2*n-1]-'a'+10;
	if(a[2*n]>='0'&&a[2*n]<='9') y=a[2*n]-'0';
	else y=a[2*n]-'a'+10;
	for(int i=4;i>=1;i--){
		if(x%2==1)
			six[i]=1;
		else six[i]=0;
		x/=2;
	}
	for(int i=8;i>=5;i--){
		if(y%2==1)
			six[i]=1;
		else six[i]=0;
		y/=2;
	}
	num++;
}

依题意,被压缩的部分分为引导域和数据域,我们需要先解压所给数据的引导域。

引导域分布在输入数据的最前几位,如果最高位为1就一直接着循环,直到找到最高位为0的第一个字节。由于数据的第一位用做判断引导域是否结束,所以每个字节只有后7位参与到引导域大小的计算。

int n=0;    //n表示解压后数据应有的长度
for(int i=1;;i++){
	change(i);
	if(six[1]==1){
		n+=getnum(2,8)*pow(128,i-1);
	}
	else if(six[1]==0){
		n+=getnum(2,8)*pow(128,i-1);
		break;
	}
}

处理完引导域后我们便得知了解压后数据应有的长度,可以接着对后面的数据域进行处理。

数据域中的字节分为字面量以及回溯引用两种,其中回溯引用也分为两种不同的情况。判断这三种不同字节的方式是将它们转换为2进制后看第七位以及第八位的值。由于每个字节的后两位参与判断种类,所以只有前6位会参与运算。

由于需要进行回溯应用的输出(可以理解为输出已经已经输出过的重复数据),所以我们需要对已经输出的数据进行存储

char a[4000005],pri[4000005];    //a为存储的输入数据,pri存储已经输出过的数据

当第7位==0&&第8位==0时,这个字节为字面量,计算出字面量后要直接进行输出。

if(six[7]==0&&six[8]==0){
	int l=getnum(1,6);
	if(l<60) nu=l+1;
	else{
		l-=59;
		for(int i=1;i<=l;i++){
			change(num);
			nu+=getnum(1,8)*pow(256,i-1);
		}
		nu++;
	}
	for(int i=num;i<=num+nu-1;i++){
		printf("%c%c",a[2*i-1],a[2*i]);
		print++;
		if(print%8==0) printf("\n");
		pri[2*print-1]=a[2*i-1];
		pri[2*print]=a[2*i];
	}
	num+=nu;
}

第7位==0&&第8位==1以及第7位==1&&第8位==0(回溯引用的两种情况),这种情况写需要先计算出偏移量o以及长度l后再输出

计算过程:

else if(six[7]==0&&six[8]==1){
	l=4;
    l+=getnum(4,6);
	o+=getnum(1,3)*256;
	change(num);
	o+=getnum(1,8);
}
else if(six[7]==1&&six[8]==0){
	l=1;
	l+=getnum(1,6);
	change(num);
	o+=getnum(1,8);
	change(num);
	o+=getnum(1,8)*256;
}

输出回溯引用数据:

if(l!=0&&o!=0){
	if(o>=l){
		int begin=print;
		for(int i=begin-o+1;i<=begin-o+l;i++){
			printf("%c%c",pri[2*i-1],pri[2*i]);
			print++;
			if(print%8==0) printf("\n");
			pri[2*print-1]=pri[2*i-1];
			pri[2*print]=pri[2*i];
		}
	}
	else{
		int begin=print;
		for(int i=1;i<=l/o;i++){
			for(int j=begin-o+1;j<=begin;j++){
				printf("%c%c",pri[2*j-1],pri[2*j]);
				print++;
				if(print%8==0) printf("\n");
				pri[2*print-1]=pri[2*j-1];
				pri[2*print]=pri[2*j];
    		}
		}
		for(int i=begin-o+1;i<=begin-o+l%o;i++){
			printf("%c%c",pri[2*i-1],pri[2*i]);
			print++;
			if(print%8==0) printf("\n");
			pri[2*print-1]=pri[2*i-1];
			pri[2*print]=pri[2*i];
		}
	}
}

整体答案:

#include<bits/stdc++.h>
using namespace std;
int s;
char a[4000005],pri[4000005];
int six[20],num=1,print=0;
void change(int n){
	for(int i=0;i<=19;i++) six[i]=0;
	int x,y;
	if(a[2*n-1]>='0'&&a[2*n-1]<='9') x=a[2*n-1]-'0';
	else x=a[2*n-1]-'a'+10;
	if(a[2*n]>='0'&&a[2*n]<='9') y=a[2*n]-'0';
	else y=a[2*n]-'a'+10;
	for(int i=4;i>=1;i--){
		if(x%2==1)
			six[i]=1;
		else six[i]=0;
		x/=2;
	}
	for(int i=8;i>=5;i--){
		if(y%2==1)
			six[i]=1;
		else six[i]=0;
		y/=2;
	}
	num++;
}
int getnum(int l,int r){
	int num=0;
	for(int i=l;i<=r;i++){
		num*=2;
		num+=six[i];
	}
	return num;
}
int main(){
	scanf("%d",&s);
	for(int i=1;i<=s/8;i++){
		for(int j=1;j<=8;j++){
			scanf(" %c%c",&a[num],&a[num+1]);
			num+=2;
		}
	}
	for(int i=1;i<=s%8;i++){		
		scanf(" %c%c",&a[num],&a[num+1]);
		num+=2;
	}
	num=1;
	int n=0;
	for(int i=1;;i++){
		change(i);
		if(six[1]==1){
			n+=getnum(2,8)*pow(128,i-1);
		}
		else if(six[1]==0){
			n+=getnum(2,8)*pow(128,i-1);
			break;
		}
	}	
	while(print!=n){
		change(num);
		int nu=0,l=0,o=0;
		if(six[7]==0&&six[8]==0){
			int l=getnum(1,6);
			if(l<60) nu=l+1;
			else{
				l-=59;
				for(int i=1;i<=l;i++){
					change(num);
					nu+=getnum(1,8)*pow(256,i-1);
				}
				nu++;
			}
			for(int i=num;i<=num+nu-1;i++){
				printf("%c%c",a[2*i-1],a[2*i]);
				print++;
				if(print%8==0) printf("\n");
				pri[2*print-1]=a[2*i-1];
				pri[2*print]=a[2*i];
			}
			num+=nu;
		}
		else if(six[7]==0&&six[8]==1){
			l=4;
			l+=getnum(4,6);
			o+=getnum(1,3)*256;
			change(num);
			o+=getnum(1,8);
		}
		else if(six[7]==1&&six[8]==0){
			l=1;
			l+=getnum(1,6);
			change(num);
			o+=getnum(1,8);
			change(num);
			o+=getnum(1,8)*256;
		}
		if(l!=0&&o!=0){
			if(o>=l){
				int begin=print;
				for(int i=begin-o+1;i<=begin-o+l;i++){
					printf("%c%c",pri[2*i-1],pri[2*i]);
					print++;
					if(print%8==0) printf("\n");
					pri[2*print-1]=pri[2*i-1];
					pri[2*print]=pri[2*i];
				}
			}
			else{
				int begin=print;
				for(int i=1;i<=l/o;i++){
					for(int j=begin-o+1;j<=begin;j++){
						printf("%c%c",pri[2*j-1],pri[2*j]);
						print++;
						if(print%8==0) printf("\n");
						pri[2*print-1]=pri[2*j-1];
						pri[2*print]=pri[2*j];
					}
				}
				for(int i=begin-o+1;i<=begin-o+l%o;i++){
					printf("%c%c",pri[2*i-1],pri[2*i]);
					print++;
					if(print%8==0) printf("\n");
					pri[2*print-1]=pri[2*i-1];
					pri[2*print]=pri[2*i];
				}
			}
		}
	}
	return 0;
}

感觉输出可以单独写一个函数,这样能少敲几十行,懒得改了。。。。。。

 

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值