SPOJ5153 Compressed String

本文介绍了一种解决压缩字符串比对问题的算法。通过使用二分查找与哈希技术,该算法能够在较短时间内确定两个压缩字符串首次出现差异的位置。文章还提供了一种递归解压方法作为对比。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

http://www.spoj.com/problems/COMPRESS/

题目简述

定义字符串的压缩表示[S]N,它等价于N 个S  串从左向右依次连接起来的新串。压缩表示可以嵌套,即S 本身可以是压缩过的字符串。 

如“[a]2”代表“aa”,“[[ab]2c]2”代表“ababcababc”。 

给出两个压缩以后的字符串S1 和S2,判断它们在哪一位开始不相同。 

对于每组字符串,如果S1 和S2 完全相同,输出“Yes”,否则输出“NO”+它们第一个不相同的位置。 

Sample Input  

a[a]12
[[a]3]4a 
[z]12 
zzzzzzzzz 
[a[ba]2b]12 
[ab]36 
[a]123123123[icpc]2 
[[a]123]1001001inter 
aismoreeasierthanc 
gismuchharderthanj 
 

Sample Output 

Case #1: YES 

Case #2: NO 10 
Case #3: YES 
Case #4: NO 123123125 
Case #5: NO 1 
 



神级恶心题。。鼓捣了一个上午才搞出来。。

先说一下暴力解压吧。

可以递归做,每个括号递归一次。

先上个暴力代码:

#include <iostream>
#include <cstdio>
#include <cstring>
#include <string>
#include <algorithm>
using namespace std;
#define rep(i,l,r) for (int i=l;i<=r;++i)
string a;
int getx(int &x,int v){
	for (x=0;a[v]>='0'&&a[v]<='9';v++)
		x=(x<<3)+(x<<1)+a[v]-'0';
	return v;
}
string zip(int l,int r,int p){
	string res="";
	rep(i,l,r){
		if (a[i]>='a'&&a[i]<='z') res+=a[i];
		if (a[i]=='['){
			int cnt=1;
			rep(j,i+1,r){
				if (a[j]=='[') cnt++;
				if (a[j]==']') cnt--;
				if (!cnt){
					int tp;
					int pos=getx(tp,j+1)-1;
					if (tp!=0) res+=zip(i+1,j-1,tp);
					i=pos;
					break;
					}
				}
			}
		}
	string tp="";
	rep(i,1,p) tp+=res;
	return tp;
}
string t1,t2;
int main(){
	freopen("compress.in","r",stdin);
	freopen("compress.out","w",stdout);
	int T;scanf("%d",&T);
	while (T--){
		cin>>a;
		t1=zip(0,a.length()-1,1);
		t1+='*';
		cin>>a;
		t2=zip(0,a.length()-1,1);
		t2+='*';
		bool pd=false;
		rep(i,0,t1.length()-1)
			if (t1[i]!=t2[i]){pd=true;printf("%d\n",i+1);break;}
		if (!pd) printf("Yes\n");
		}
}


标算:二分+哈希。压缩串用二叉树装。

基本思路:二分位置,用哈希判断前缀是否同。

现在问题就是如何算一个前缀的哈希。

和上面暴力解压的方法类似,递归建树,但是不保留串,只保留子树串的哈希值。

我写的有点麻烦。。。不说了。。

算哈希要运用的式子:

令Hash(x)表示串x的哈希值。Hash(x*a)表示具有连续a个x的串的哈希值。Hash(x+y)表示串xy拼接起来的哈希值。|x|表示x的串长。

当然都是MOD mod意义下的运算。。mod最好是个质数,要不然第二个式子不好算。



上面的分母算一个逆元



。。。好恶心啊真的此题爽啊,,上代码。。


#include <cstdio>
#include <cstring>
#include <algorithm>
#define rep(i,l,r) for (int i=l;i<=r;++i)
#define per(i,r,l) for (int i=r;i>=l;--i)
using namespace std;
typedef long long LL;
const int MAX_L=25;
const LL MOD=1000000007LL;
const LL base=127LL;
LL power(LL a,LL b){
	LL res=1;b%=MOD-1;
	for (;b;a=a*a%MOD,b>>=1)
		if (b&1) res=res*a%MOD;
	return res;
}
struct Hash{
	LL hash,len;
	Hash(LL _h = 0 ,LL _l = 0):hash(_h),len(_l){}
	void pt(){printf("%I64d %I64d\n",hash,len);}
};
bool operator ==(const Hash &a,const Hash &b){
	return a.hash==b.hash&&a.len==b.len;
}
Hash operator +(const Hash &a,const Hash &b){
	return Hash((a.hash*power(base,b.len)+b.hash)%MOD,a.len+b.len);
}
Hash operator *(const Hash &a,LL b){
	return Hash(((a.hash*(power(base,a.len*b)-1+MOD))%MOD*power(power(base,a.len)-1+MOD,MOD-2))%MOD,a.len*b);
}
Hash gt(const char *s){
	Hash res;
	res.len=strlen(s);
	rep(i,0,res.len-1) res.hash=res.hash*base%MOD+s[i];
	return res;
}
struct Tree{
	char s[MAX_L];
	int lc[MAX_L*100],rc[MAX_L*100];
	int n,root,tal;
	LL w[MAX_L*100];
	Hash h[MAX_L*100];
	void set(int &x,int _tal,LL _w){x=_tal;w[x]=_w;lc[x]=rc[x]=0;}
	void init(){
		tal=1;set(root,tal,1);
		scanf("%s",s+1);
		build(root,1,n=strlen(s+1));
		}
	int findr(int l){
		int cnt=1;
		rep(i,l+1,n){
			if (s[i]=='[') cnt++;
			if (s[i]==']') cnt--;
			if (!cnt) return i;
			}
		}
	int getn(LL &x,int l){
		for (x=0;s[l]>='0'&&s[l]<='9';l++)
			x=(x<<3)+(x<<1)+s[l]-'0';
		return l;
		}
	Hash gtpre(int v,LL l){
		if (l==1&&h[v].len==1) return h[v];
		if (l==0) return Hash(0,0);
		if (l>h[lc[v]].len*w[lc[v]]) return h[lc[v]]*w[lc[v]]+gtpre(rc[v],l-w[lc[v]]*h[lc[v]].len);
		else return h[lc[v]]*(l/h[lc[v]].len)+gtpre(lc[v],l%h[lc[v]].len);
		}
	void build(int &v,int l,int r){
		if (l>r) {w[v]=0;return;}
		if (l==r){
			h[v]=Hash(s[l],1);
			return;
			}
		if (s[l]=='['){
			int p1=findr(l),p2;LL x;
			p2=getn(x,p1+1);
			if (x!=0){
				set(lc[v],++tal,x);
				build(lc[v],l+1,p1-1);
				}
			set(rc[v],++tal,1);
			build(rc[v],p2,r);
		 }else{
			set(lc[v],++tal,1);
			build(lc[v],l,l);
			set(rc[v],++tal,1);
			build(rc[v],l+1,r);
			}
		h[v]=h[lc[v]]*w[lc[v]]+h[rc[v]]*w[rc[v]];
		}
};
Tree t1,t2;
bool check(LL mid){
	return t1.gtpre(1,mid)==t2.gtpre(1,mid);
}
void solve(){
	if (t1.h[1]==t2.h[1]){printf("YES\n");return;}
	LL l=1,r=std::min(t1.h[1].len,t2.h[1].len);
	if (check(r)){printf("NO %lld\n",r+1);return;}
	if (!check(l)){printf("NO 1\n");return;}
	while(r-l>1){
		LL mid=l+r>>1;
		if (check(mid)) l=mid;
			else r=mid;
		}
	printf("NO %lld\n",r);
}
int main(){
	freopen("compress.in","r",stdin);
	freopen("compress.out","w",stdout);
	int T;scanf("%d",&T);
	rep(i,1,T){
		t1.init(),t2.init();
		printf("Case #%d: ",i);solve();
		}
}




评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值