2019.03.26【洛谷P5108】仰望半月的星空(单调栈)(Hash二分)

版权声明:转载请声明出处,谢谢配合。 https://blog.csdn.net/zxyoi_dreamer/article/details/88812977

传送门


解析:

首先可以很显然的发现长度为 n n 的答案肯定为 1 1

考虑倒着处理。

我们发现,在长度变短的过程中,有一些串逐渐变得相同,如果在某个长度 l e n len s i s j s_i \leq s_j ,且 i < j i<j ,(其中 s i s_i 表示以 i i 开头,且长度为当前讨论长度的子串。)则 j j 在之后都不可能成为任何位置的答案,因为总有一个字典序小于等于它的 i i 在它左边。

单调栈维护这个关系就行了。

比较字典序我采用的是Hash+二分。


代码:

#include<bits/stdc++.h>
#define ll long long
#define re register
#define gc getchar
#define cs const

namespace IO{
	inline int getint(){
		re char c;
		while(!isdigit(c=gc()));re int num=c^48;
		while(isdigit(c=gc()))num=(num+(num<<2)<<1)+(c^48);
		return num;
	}
}
using namespace IO;

using std::cerr;
using std::cout;
using std::swap;

cs int N=3e5+5;

int n,m;
char s[N];
int a[N];

namespace Hash{
	typedef unsigned long long u64;
	cs u64 Base=47,Base2=131;
	u64 h[N],Pow[N];
	inline void init(){
		Pow[0]=1;
		for(int re i=1;i<=n;++i){
			Pow[i]=Pow[i-1]*Base;
			h[i]=h[i-1]*Base+a[i];
		}
	}
	
	inline u64 get(int l,int r){
		return h[r]-h[l-1]*Pow[r-l+1];
	}
}

inline bool equal(int l1,int l2,int len){
	return Hash::get(l1,l1+len-1)==Hash::get(l2,l2+len-1);
}

inline bool cmp(int l1,int l2,int len){
	if(equal(l1,l2,len))return true;
	int l=1,r=len,ans=0;
	while(l<=r){
		int mid=(l+r)>>1;
		if(equal(l1,l2,mid))l=mid+1,ans=mid;
		else r=mid-1;
	}
	return a[l1+ans]<=a[l2+ans];
}

int sta[N],top;
int ans[N];

signed main(){
	std::ios::sync_with_stdio(false);
	m=getint();n=getint();
	if(m==26){
		scanf("%s",s+1);
		for(int re i=1;i<=n;++i)a[i]=s[i];
	}
	else for(int re i=1;i<=n;++i)a[i]=getint();
	Hash::init();
	for(int re i=1;i<=n;++i){
		sta[++top]=i;
		while(top>1&&cmp(sta[top-1],sta[top],n-i+1))--top;
		ans[n-i+1]=sta[top];
	}
	for(int re i=1;i<=n;++i)cout<<ans[i]<<" ";
	return 0;
}

猜你喜欢

转载自blog.csdn.net/zxyoi_dreamer/article/details/88812977
今日推荐