zoukankan      html  css  js  c++  java
  • bzoj 4278 Tasowanie 后缀数组+贪心

    题目大意

    给定两个数字串A和B,通过将A和B进行二路归并得到一个新的数字串T,请找到字典序最小的T.(len leq 200000)

    题解

    我们从归并排序的角度去想,每次把两者之一较小的取出来
    遇到相等的元素的时候,排序时取那个都是一样的.
    但是在这道题中我们是求字典序最小,这就不一样了.
    我们不能任意取,这一步的决策其实是由后面的数字的大小决定的
    怎么说呢...
    方法蕴藏在这三张图里了,自己去悟吧。
    黑色箭头表示选取,黑色圆圈表示确定选取序列的理由.
    迭代第一次

    迭代第二次

    迭代第三次

    剩下的略...
    这个东西我们可以用后缀数组(lcp)来维护

    Code

    #include <cstdio>
    #include <cstring>
    #include <algorithm>
    using namespace std;
    inline void read(int &x){
    	x=0;char ch;bool flag = false;
    	while(ch=getchar(),ch<'!');if(ch=='-')ch=getchar(),flag = true;
    	while(x=10*x+ch-'0',ch=getchar(),ch>'!');if(flag) x=-x;
    }
    const int maxn = 410010;
    int a[maxn],b[maxn],c[maxn];
    int wa[maxn],wb[maxn],ws[maxn];
    int sa[maxn],rank[maxn],height[maxn];
    inline bool cmp(int *r,int i,int j,int l){
    	return r[i] == r[j] && r[i+l] == r[j+l];
    }
    void da(int *r,int n,int m){
    	int i,j,p,*x = wa,*y = wb;
    	for(i=0;i<m;++i) ws[i] = 0;
    	for(i=0;i<n;++i) ws[x[i] = r[i]]++;
    	for(i=1;i<m;++i) ws[i] += ws[i-1];
    	for(i=n-1;i>=0;--i) sa[--ws[x[i]]] = i;
    	for(j=1,p=1;p<n;j<<=1,m=p){
    		for(p=0,i=n-j;i<n;++i) y[p++] = i;
    		for(i=0;i<n;++i) if(sa[i] >= j) y[p++] = sa[i] - j;
    		for(i=0;i<m;++i) ws[i] = 0;
    		for(i=0;i<n;++i) ws[x[y[i]]]++;
    		for(i=1;i<m;++i) ws[i] += ws[i-1];
    		for(i=n-1;i>=0;--i) sa[--ws[x[y[i]]]] = y[i];
    		for(swap(x,y),p=1,i=1,x[sa[0]] = 0;i<n;++i)
    		x[sa[i]] = cmp(y,sa[i-1],sa[i],j) ? p-1 : p++;
    	}
    }
    void get_h(int *r,int n){
    	int i,j,k=0;for(i=1;i<=n;++i) rank[sa[i]] = i;
    	for(i=0;i<n;height[rank[i++]] = k)
    	for(k ? --k : 0,j = sa[rank[i]-1];r[i+k] == r[j+k];++k);
    }
    int loger[maxn],minn[maxn][22];
    void pre(int *r,int n){
    	loger[1] = 0;
    	for(int i=2;i<=n;++i){
    		loger[i] = loger[i-1];
    		if( (1 << loger[i]+1 ) == i) ++loger[i];
    	}
    	for(int i=n;i>=1;--i){
    		minn[i][0] = r[i];
    		for(int j=1;(i + (1<<j) - 1) <= n;++j){
    			minn[i][j] = min(minn[i][j-1],minn[i+(1<<j-1)][j-1]);
    		}
    	}
    }
    int lcp(int s,int t){
    	s = rank[s];t = rank[t];
    	if(s+1 > t) swap(s,t);++s;
    	int k = loger[t-s+1];	
    	return min(minn[s][k],minn[t-(1<<k)+1][k]);
    }
    int n,m,len;
    inline bool order(int i,int j){
    	int k = lcp(i,j);
    	int x = (i+k) == n   ? 0x7f7f7f7f : c[i+k];
    	int y = (j+k) == len ? 0x7f7f7f7f : c[j+k];
    	return x <= y;
    }
    int q[maxn];
    int main(){
    	read(n);
    	for(int i=0;i<n;++i){
    		read(a[i]);
    		c[i] = a[i];
    	}c[n] = 1005;
    	read(m);
    	for(int i=0;i<m;++i){
    		read(b[i]);
    		c[n+i+1] = b[i];
    	}len = n+m+1;c[len] = 0;
    	da(c,len+1,1010);
    	get_h(c,len);
    	pre(height,len);
    	int i=0,j=0,k=0;
    	while(i < n && j < m){
    		int x = order(i,j+n+1);
    		if(x == 1) q[k++] = a[i++];
    		else q[k++] = b[j++];
    	}
    	while(i < n) q[k++] = a[i++];
    	while(j < m) q[k++] = b[j++];
    	for(i=0;i<k;++i) printf("%d ",q[i]);
    	getchar();getchar();
    	return 0;
    }
    
  • 相关阅读:
    centos6.x 配置bond
    Js学习(2)
    Js学习(1)
    Java源码阅读计划(1) String<II>
    【461】汉明距离
    【617】合并二叉树
    Java源码阅读计划(1) String<I>
    Dubbo的高可用性
    Dubbo SpringBoot配置方法
    Dubbo基本配置属性
  • 原文地址:https://www.cnblogs.com/Skyminer/p/6407439.html
Copyright © 2011-2022 走看看