【ByteDance冬令营】DAY 3 总结 2019.2.18

contest

I

先从I说起:
题意:求lcp(i,j) == k,要求lcp字典序最小,在此条件下有序对(i,j)最小。

这道题本来是应该30min内AC的,
首先,看到题太激动,没有看样例,连字典序最小的要求都没有看到就开始写了。这样没有理清细节就开始写一定不可能一次AC!
之后找到了正解,忘了判0,然后还是WA。完全找不到错,开始对拍。
然而对拍的时候粘了以前的输出(其实是完全没有意识到错误),特判0的时候:

	if ( !ans[p].len ){ //不能直接用lcp的长度特判是否有答案,长度是0不就错了吗。
			if ( !p ) puts("-1 -1");
			else printf("%d %d\n",n - p + 1,n - p + 1);
		}
		else{
			node2 tmp = (node2){n - p + 1,n - p + 1,rk[n - p],p};
			if ( tmp < ans[p] )  printf("%d %d\n",n - p + 1,n - p + 1);
			else printf("%d %d\n",ans[p].id1,ans[p].id2);
		}		

更不应该的是,手造了明显会WA的数据,然而想当然的以为0的时候就是-1,-1
其实只有所有字母相同时才是-1,-1!!!
必须仔细的反思自己思维的每个细节,才能找到错!
这道题考场上调了1h多,下来调了25min,就因为这样一个小错误。只有真正仔细才能避免!

说下这题正解:
求出后缀数组,按照h[i]倒序合并,每次可以选出的两块的后缀位置最小的更新答案。至于lcp字典序,因为求出后缀数组了,就很好比较了。(注意不能只看在后缀数组中的位置,因为lcp可能相同)
当然可以用后缀树,在lca处更新答案,但是lcp的字典序比较稍微有点麻烦

#include <bits/stdc++.h>
#define rep(i,a,b) for(int i=a;i<=b;++i)
#define per(i,a,b) for(int i=a;i>=b;--i)
#define repd(i,a,b) for(int i=a;i>=b;--i)
#define rvc(i,S) for(int i=0;i<S.size();++i)
#define fore(i,x) for(int i = head[x] ; i ; i = e[i].next)
#define mp make_pair
#define pb push_back
#define fi first
#define se second
#define debug(...) fprintf(stderr, __VA_ARGS__)
using namespace std;

const int maxn = 200020;
char s[maxn];
int n,c[maxn],t1[maxn * 2],t2[maxn * 2],sa[maxn],rk[maxn],h[maxn];
int mn[20][maxn * 2],cnt[maxn * 2];

struct node{
	int l,id;
	bool operator < (node a)const{
		if ( l == a.l ) return id < a.id;
		return l < a.l;
	}
}dt[maxn];

int tot;
void suffix_array(){
	int m = 200 , *x = t1 , *y = t2;
	rep(i,0,m) c[i] = 0;
	rep(i,0,n - 1) c[x[i] = s[i]]++;
	rep(i,1,m) c[i] += c[i - 1];
	rep(i,0,n - 1) sa[--c[x[i]]] = i;
	for (register int k = 1 ; k < n ; k <<= 1){
		int p = 0;
		memset(y,0,sizeof(t1));
		repd(i,n - 1,n - k) y[p++] = i;
		rep(i,0,n - 1) if ( sa[i] >= k ) y[p++] = sa[i] - k;
		
		rep(i,0,m) c[i] = 0;
		rep(i,0,n - 1) c[x[y[i]]]++;
		rep(i,1,m) c[i] += c[i - 1];
		repd(i,n - 1,0) sa[--c[x[y[i]]]] = y[i];
		
		p = 0 , swap(x,y) , x[sa[0]] = ++p;
		rep(i,1,n - 1) x[sa[i]] = (y[sa[i]] == y[sa[i - 1]]) && (y[sa[i] + k] == y[sa[i - 1] + k]) ? p : ++p;
		if ( p >= n ) break;
		m = p;
	}
	rep(i,0,n - 1) rk[sa[i]] = i;
	int k = 0;
	rep(i,0,n - 1){
		if ( !rk[i] ) continue;
		int j = sa[rk[i] - 1];
		if ( k ) k --;
		while ( s[j + k] == s[i + k] ) k++;
		h[rk[i]] = k;
		dt[++tot] = (node){k,rk[i]};
	}
	sort(dt +1,dt + tot + 1);
	//rep(i,1,tot) cout<<dt[i].l<<" "<<dt[i].id<<endl;
	//cout<<endl<<endl;
}
inline int lcp(int x,int y){
	if ( x == y ) return n;
	if ( x > y ) swap(x,y);
	x++;
	int c = cnt[y - x + 1];
	return min(mn[c][x],mn[c][y - (1 << c) + 1]);
}
struct node2{
	int id1,id2,pos,len,tag;
	//node2(){};
	bool operator < (node2 a)const{
	int l2 = lcp(pos,a.pos);
		if ( l2 >= len ){ // the lcp is equal
			if ( id1 == a.id1 ) return id2 < a.id2;
			return id1 < a.id1;
		}
		return s[sa[pos] + l2] < s[sa[a.pos] + l2];
	}
}ans[maxn];
int mn2[maxn],fa[maxn];

void init(){
	int k = 0;
	rep(i,0,n){
		if ( i > (1 << (k + 1)) ) k++;
		cnt[i] = k;
	}
	rep(i,0,n - 1) mn[0][i] = h[i];
	rep(i,1,19)
		rep(j,0,n - 1)
			if ( j + (1 << (i - 1)) < n ) mn[i][j] = min(mn[i - 1][j],mn[i - 1][j + (1 << (i - 1))]);
			else mn[i][j] = mn[i - 1][j];
}
int getfa(int x) { return x == fa[x] ? x : fa[x] = getfa(fa[x]);}
void init_ans(){
	rep(i,0,n) fa[i] = i , mn2[i] = sa[i] + 1;
	repd(i,tot,1){
		int id = dt[i].id , len = dt[i].l;
		int p = getfa(id - 1) , q = getfa(id);
	
		node2 cur = (node2){mn2[p],mn2[q],id,len,1};
		if ( cur.id1 > cur.id2 ) swap(cur.id1,cur.id2);
		if ( !ans[len].tag || cur < ans[len] ) ans[len] = cur;
		fa[p] = q , mn2[q] = min(mn2[q],mn2[p]);
	}
}
int main(){
//	freopen("input.txt","r",stdin);
	
	scanf("%s",s);
	n = strlen(s);
	suffix_array();	
	init();
	init_ans();
	int m;
	scanf("%d",&m);
	while ( m-- ){
		int p;
		scanf("%d",&p);
		if ( !ans[p].tag ){
			if ( !p ) puts("-1 -1");
			else printf("%d %d\n",n - p + 1,n - p + 1);
		}
		else{
			node2 tmp = (node2){n - p + 1,n - p + 1,rk[n - p],p};
			if ( tmp < ans[p] )  printf("%d %d\n",n - p + 1,n - p + 1);
			else printf("%d %d\n",ans[p].id1,ans[p].id2);
		}		
	}
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值