[Ceoi2016|BZOJ4936] Match
哈希+分治+stack
题目:
给你一个由小写字母组成的字符串s,要你构造一个字典序最小的(认为左括号的字典序比右括号小)合法的括号
序列与这个字符串匹配,字符串和括号序列匹配定义为:首先长度必须相等,其次对于一对匹配的左括号和右括号
i,j,必须有s[i]==s[j]
无解输出-1
很坑:括号配对原来是最近的左括号右括号算一对...我以为随便怎么配只要左括号右括号数量相等且能左右分离就好了...
首先是判断无解情况:开一个stack,碰到和top相同的弹掉,如果最后stack不空就必然无解
然后是分治即可
显然对于Solve(l,r)的l元素的'('我们要找离其最远的')',当然这是要可行的前提下
难点是如何判断可行情况:在记录stack的情况下维护hash,出栈减掉,入栈加上,当Hash(l-1)==Hash(r)的时候(l,r)是可行区间
至于分治写起来就不难了
std好像是map,但是昨天写了一晚上的hash,就直接打hash了
而且用map在这里反而比hash要复杂!
代码:
1 #include<bits/stdc++.h> 2 #define ull unsigned long long 3 using namespace std; 4 inline int read(){ 5 int ans=0,f=1;char chr=getchar(); 6 while(!isdigit(chr)){if(chr=='-')f=-1;chr=getchar();} 7 while(isdigit(chr)) {ans=(ans<<3)+(ans<<1)+chr-48;chr=getchar();} 8 return ans*f; 9 }const int M=1e5+5,B=131; 10 ull f[M],p[M],now; 11 char s[M],st[M],ans[M]; 12 int top,n; 13 void Solve(int l,int r){ 14 if(l>=r) return; 15 for(int p=r;;p--) 16 if(s[l]==s[p]&&f[l-1]==f[p]&&(p==r||f[p]==f[r])){ 17 ans[l]='(',ans[p]=')'; 18 Solve(l+1,p-1); 19 p<r?Solve(p+1,r):Solve(2,1); 20 break; 21 } 22 } 23 int main(){ 24 freopen("match.in","r",stdin); 25 freopen("match.out","w",stdout); 26 scanf("%s",s+1); 27 n=strlen(s+1); 28 p[0]=1; 29 for(int i=1;i<=n;i++) p[i]=p[i-1]*B; 30 for(int i=1;i<=n;i++){ 31 if(s[i]==st[top]) now-=p[top--]*(s[i]-'a'+1); 32 else now+=p[++top]*(s[i]-'a'+1),st[top]=s[i]; 33 f[i]=now; 34 }if(top) return puts("-1"),0; 35 Solve(1,n); 36 printf("%s",ans+1); 37 return 0; 38 }