CF524F And Yet Another Bracket Sequence【哈希+二分或后缀数组+线段树】

版权声明:转载无所谓的。。。 https://blog.csdn.net/xuxiayang/article/details/82870591

链接

https://www.luogu.org/problem/show?pid=CF524F


题目大意

给定有一个长度为 n n 的括号序列,现在有两种操作:

  1. 在任意一个位置插入有一个左括号或右括号
  2. 将末尾的一个括号放到最前面

可以对这个序列进行若干次操作,问在使括号序列合法的前提下,长度最短是多少,如果有多组解,输出字典序最小的

N 1 0 6 N\leq10^6


解题思路

可以用后缀数组+线段树解决,但这种方法码很长~~(很难抠标)~~,考虑其它的算法

首先最后的长度一定等于(原字符串长度+左括号与右括号数量的差值),现在我们考虑让其的字典序尽量的小

我们预处理前缀和,’(‘为+1,’)'为-1

我们可以发现,最终的答案一定是从序列中的某一位开始循环 n n 次,最后补上需要补足的括号数的,然后我们就只需要查找长度为 n n 的子串中合法且最小的那串

判断合法的过程可以用hash+二分


97%(38/39)代码

自然溢出哈希,但在倒数第二个点hash值出现了奇怪的重复现象,所以该代码过不了倒数第二个点

但是该代码在于正解的对拍中10000组数据内都没有误差。。。

代码解释在正解那里

#include<queue>
#include<cstdio>
#include<cstring>
#define r(i,a,b) for(register int i=a;i<=b;i++)
#define MAXN 2000005
using namespace std;char s[MAXN];
int n,N,a[MAXN],p,tot;
unsigned long long c[MAXN],h[MAXN];
deque<int>q;
const int base=3;
inline unsigned long long gethash(register int x,register int len){return h[x+len-1]-h[x-1]*c[len];}
inline bool cmp(register int x,register int y)
{
	int l=0,r=n,ans=0,mid;
	while(l<=r)
	{
		mid=l+r>>1;
		if(gethash(x,mid)==gethash(y,mid)) l=(ans=mid)+1;else r=mid-1;
	}
	return ans==n?1:s[x+ans]=='(';
}
signed main()
{
    /*freopen("data.in","r",stdin);
    freopen("假解.out","w",stdout);用来对拍的,见谅!*/
	scanf("%s",s+1);n=strlen(s+1);N=n<<1;
	c[0]=1;
	r(i,1,n) s[i+n]=s[i],tot+=(s[i]=='('?1:-1);
	r(i,1,N)
	{
		c[i]=c[i-1]*base;
		h[i]=h[i-1]*base+(s[i]=='('?1:2);
	}
	if(tot>=0)
	{
		r(i,1,N) a[i]=a[i-1]+(s[i]=='('?1:-1);
		r(i,1,N-1)
		{
			while(q.size()&&a[i]<=a[q.back()]) q.pop_back();
			q.push_back(i);
			while(q.size()&&i-q.front()>=n) q.pop_front();
			if(q.size()&&i>=n&&a[q.front()]>=a[i-n]) {if(!p||cmp(i-n+1,p)) p=i-n+1;}
		}
		for(register int i=0;i<n;i++) putchar(s[i+p]);
		r(i,1,tot) putchar(')');
	}
	else
	{
		for(register int i=N;i;i--) a[i]=a[i+1]-(s[i]=='('?1:-1);
		for(register int i=N-1;i;i--)
		
		{
			while(q.size()&&a[i]<=a[q.back()]) q.pop_back();
			q.push_back(i);
			while(q.size()&&q.front()-i>=n) q.pop_front();
			if(q.size()&&i<=n&&a[q.front()]>=a[i+n]) if(!p||cmp(i,p)) p=i;
		}
		for(register int i=tot;i<0;i++) putchar('(');
		for(register int i=0;i<n;i++) putchar(s[i+p]);
	}
}

AC代码

对上版本的 h a s h hash 的改良版本,人工处理溢出

#include<queue>
#include<cstdio>
#include<cstring>
#define ymw 1000000009
#define r(i,a,b) for(register int i=a;i<=b;i++)
#define MAXN 2000005
using namespace std;char s[MAXN];
int n,N,a[MAXN],p,tot,c[MAXN],h[MAXN];
deque<int>q;
inline int add(register int x,register int y){x+=y;return x>=ymw?x-ymw:x;}//人工处理溢出
inline int gethash(register int x,register int len){return add(h[x+len-1],ymw-h[x-1]*(long long)c[len]%ymw);}//取hash值
inline bool cmp(register int x,register int y)//判断以x开头的串是否能替换y开头的串
{
	int l=0,r=n,ans=0,mid;
	while(l<=r)
	{
		mid=l+r>>1;
		if(gethash(x,mid)==gethash(y,mid)) ans=mid,l=mid+1;else r=mid-1;
	}
	return ans==n?1:s[x+ans]=='(';//完全能替换或者不匹配的位置是左括号都行(这表明y串不合法)
}
inline void solve1()//处理左括号更多的情况,最后补右括号
{
	c[0]=1;
	r(i,1,N)
	{
		a[i]=a[i-1]+(s[i]=='('?1:-1);
		c[i]=add(c[i-1],add(c[i-1],c[i-1]));
		h[i]=add(add(h[i-1],add(h[i-1],h[i-1])),(s[i]=='('?1:2));
	}
	r(i,1,N-1)
	{
		while(q.size()&&a[i]<=a[q.back()]) q.pop_back();//优先用右括号多的进行转移
		q.push_back(i);
		while(q.size()&&i-q.front()>=n) q.pop_front();//长度大于n,弹出
		if(i>=n&&a[q.front()]>=a[i-n]) if(!p||cmp(i-n+1,p)) p=i-n+1;
	}
	for(register int i=0;i<n;i++) putchar(s[p+i]);
	r(i,1,tot) putchar(')');
	return;
}
inline void solve2()//需要补及左括号的,把原串倒过来处理即可
{
	c[0]=1;
	r(i,1,N)
	{
		c[i]=add(c[i-1],add(c[i-1],c[i-1]));
		h[i]=add(add(h[i-1],add(h[i-1],h[i-1])),(s[i]=='('?1:2));
	}
	for(register int i=N;i;i--) a[i]=a[i+1]-(s[i]=='('?1:-1);
	for(register int i=N-1;i;i--)
	{
		while(q.size()&&a[i]<=a[q.back()]) q.pop_back();
		q.push_back(i);
		while(q.size()&&q.front()-i>=n) q.pop_front();
		if(i<=n&&a[q.front()]>=a[i+n]) if(!p||cmp(i,p)) p=i;
	}
	for(register int i=tot;i<0;i++) putchar('(');
	for(register int i=0;i<n;i++) putchar(s[p+i]);
	return;
}
signed main()
{
	scanf("%s",s+1);n=strlen(s+1);N=n<<1;
	r(i,1,n) s[i+n]=s[i],tot+=(s[i]=='('?1:-1);//计算+预处理
	if(tot>=0) solve1();else solve2();
}

猜你喜欢

转载自blog.csdn.net/xuxiayang/article/details/82870591