SPOJ 7258 SUBLEX - Lexicographical Substring Search【SAM,我要报警x

8 篇文章 0 订阅
5 篇文章 0 订阅

题目大意:求不同字串中第K小的那个

反正是弦论简化版x

我觉得数据有锅【……反正弦论代码交上去WA了】

假设K<=不同子串的个数, 并在K大于这个个数时直接putchar('\n'),WA到炸裂【跪地

然而如果无论如何直接算的话…………MD过了【摊手

【然后为了这个调了好几天…………还以为自己SAM白学了x】

代码↓

#include<bits/stdc++.h>
#define MAXN 180057
//#define FLAZE_NAIVE
using namespace std;	char read_s[MAXN>>1]; int Q,K;

struct t1{
	int son[MAXN][26],pre[MAXN],dis[MAXN];
	int lst,cnt;
	int lth;
	int p,np,q,nq;
	
	void insert(int x){
		dis[np=++cnt]=dis[p=lst]+1;
		lst=np;
		for(;p&&!son[p][x];p=pre[p])	son[p][x]=np;
		if(!p)	return pre[np]=1,void();
		q=son[p][x];
		if(dis[q]==dis[p]+1)	pre[np]=q;
		else{
			dis[nq=++cnt]=dis[p]+1;
			memcpy(son[nq],son[q],sizeof son[q]);
			pre[nq]=pre[q];
			pre[q]=pre[np]=nq;
			for(;p&&son[p][x]==q;p=pre[p])	son[p][x]=nq;
		}
	}
	
	void build(){
		lst=cnt=1;
		scanf("%s",read_s);
		lth=strlen(read_s);
		for(int i=0;i<lth;++i)	insert(read_s[i]-'a');
	}


	
	int f[MAXN];
	int v[MAXN],s[MAXN];
	void prep(){
		for(int i=1;i<=cnt;++i)	++v[dis[i]];
		for(int i=1;i<=lth;++i)	v[i]+=v[i-1];
		for(int i=1;i<=cnt;++i)	s[v[dis[i]]--]=i;
		
		for(int i=cnt;i;--i){
			int k=s[i];
			f[k]=1;
			for(int j=0;j<26;++j)
				if(son[k][j])
					f[k]+=f[son[k][j]];
		}
	}

	void solve(int k){
		int now=1;
		while(k){
			for(int i=0;i<26;++i)
				if(son[now][i]){
					if(k<=f[son[now][i]]){
						now=son[now][i],putchar('a'+i),--k;
						break;
					}
					else
					k-=f[son[now][i]];
				}
		}
	}

#ifdef FLAZE_NAIVE
	char tmp[MAXN>>1];
	void dfs(int now,int stp){
		for(int i=0;i<26;++i)
			if(son[now][i]){
				tmp[stp]='a'+i;
				printf("%d ",f[son[now][i]]);
				puts(tmp);
				dfs(son[now][i],stp+1);
			}
		tmp[stp]=' ';
	}
#endif
}SAM_s;

int main(){
	SAM_s.build();
	SAM_s.prep();

#ifdef FLAZE_NAIVE 
	SAM_s.dfs(1,0);
#endif
	scanf("%d",&Q);
	while(Q--){
		scanf("%d",&K);
		SAM_s.solve(K);
		putchar('\n');
	}	
	return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值