BZOJ4936:match (不错的分治)
给你一个由小写字母组成的字符串s,要你构造一个字典序最小的(认为左括号的字典序比右括号小)合法的括号 序列与这个字符串匹配,字符串和括号序列匹配定义为:首先长度必须相等,其次对于一对匹配的左括号和右括号 i,j,必须有s[i]==s[j] 无解输出-1 Input 一行一个字符串s 2<=n<=100000 Output 一行一个括号序列或者-1 Sample Input abbaaa Sample Output (()())
#include<bits/stdc++.h> using namespace std; const int maxn=100010; int num[26],used[26]; char c[maxn],ans[maxn]; int main() { int L,i; scanf("%s",c+1); L=strlen(c+1); for(i=1;i<=L;i++) num[c[i]-'a']++; for(i=0;i<26;i++){ if(num[i]%2==1) { puts("-1"); return 0; } } for(i=1;i<=L;i++){ if(used[c[i]-'a']<num[c[i]-'a']/2) ans[i]='('; else ans[i]=')'; used[c[i]-'a']++; } for(i=1;i<=L;i++) putchar(ans[i]); return 0; }
一开始交了发贪心,WA了,才想起来贪心的话,会导致匹配的时候不一定满足s[i]==s[j] 尴尬==
然后看别人说是分治。
分治: 每次按照栈的合法性(用hash记录),对于[L,R],找到最远匹配的地方pos,使得char[L]==char[pos],且[L+1,pos-1],[pos+1,R]分别匹配。然后再以大化小解决小区间。
#include<bits/stdc++.h> using namespace std; #define ll long long const int maxn=200010; const int seed=131; const int Mod=1e9+7; map<int,int>Laxt[26]; char c[maxn],ans[maxn]; int Next[maxn],q[maxn],top,hash[maxn],fac[maxn]; void solve(int L,int R) { if(L>=R) return ; int pos=Laxt[c[L]-'a'][hash[L-1]]; while(ans[pos]=='('||ans[pos]==')') pos=Next[pos];//没加这一句TLE了很多次,why Laxt[c[L]-'a'][hash[L-1]]=Next[pos]; ans[L]='('; ans[pos]=')'; solve(pos+1,R); solve(L+1,pos-1); } int main() { int N,i,j,now=0; scanf("%s",c+1); N=strlen(c+1); fac[0]=1; for(i=1;i<=N;i++) fac[i]=(ll)fac[i-1]*seed%Mod; for(i=1;i<=N;i++){ //hash里面,最高位才可以用来加减。 if(top&&c[q[top]]==c[i]){ ((now-=(ll)fac[top-1]*(c[i]-'a'+1)%Mod))%=Mod,--top; if(now<0) now+=Mod; } else (now+=(ll)(c[i]-'a'+1)*fac[top]%Mod)%=Mod,q[++top]=i; hash[i]=now; } for(i=1;i<=N;i++){ Next[i]=Laxt[c[i]-'a'][hash[i]]; Laxt[c[i]-'a'][hash[i]]=i; } if(top!=0) { puts("-1"); return 0;} solve(1,N); for(i=1;i<=N;i++) putchar(ans[i]); return 0; }
It is your time to fight!