[bzoj4936]Match

题目大意

给你一个小写字母字符串。
请构造一个合法括号序,使得匹配的括号在原串中字母相同。
要求字典序最小,且要求判断无解。

暴力

我们考虑如何判断无解。
你考虑一个栈,顺序扫这个字符串。
假如当前字符和栈顶字符相同消除栈顶字符,否则将这个字符加进栈中。
这可以得到一个合法解,且我们可以证明任意解可以变成这个合法解。
这就是无解判断。
字典序最小的暴力也很简单。

一些性质

我们设f(l,r)=0/1表示只看l~r是否合法。
我们设s[i]表示将1~i加入栈中后栈内元素情况(用哈希值表示)。
则f(l,r)=1一定要有s[l-1]=s[r]。
我们用过程solve(l,r)表示想要给l~r构造最小字典括号序。
我们需要知道谁和l匹配。
假设为pos。
那么pos=max{x|a[x]=a[l]且f(x+1,r)=1}
然后继续递归solve(pos+1,r)和solve(l+1,pos-1)
如何找到pos成为了问题。
发现f(x+1,r)=1就是s[x]=s[r]。
对于同一哈希值同一字符,它显然是单调的,也就是这个pos在减小。
然后如果我们优先solve(pos+1,r),就可以让所有的指针都到[l,pos],于是继续递归solve(l+1,pos-1)。
然后我们就线性解决了本题。

#include<cstdio>
#include<algorithm>
#include<cstring>
#include<map>
#define fo(i,a,b) for(i=a;i<=b;i++)
using namespace std;
typedef long long ll;
typedef pair<int,int> pi;
map<pi,int> p[30];
const int maxn=100000+10,mo1=1000000007,mo2=998244353;
char s[maxn],ans[maxn];
int sta[maxn],left[maxn],mi[maxn][2];
pi sum[maxn];
int i,j,k,l,t,n,m,tot,top,now1,now2;
void solve(int l,int r){
    if (l>r) return;
    ans[l]='(';
    int pos=p[s[l]-'a'][sum[l-1]];
    while (ans[pos]=='('||ans[pos]==')') pos=left[pos];
    p[s[l]-'a'][sum[l-1]]=left[pos];
    ans[pos]=')';
    solve(pos+1,r);
    solve(l+1,pos-1);
}
int main(){
    freopen("data.in","r",stdin);freopen("wzd.out","w",stdout);
    scanf("%s",s+1);
    n=strlen(s+1);
    mi[0][0]=mi[0][1]=1;
    fo(i,1,n){
        mi[i][0]=(ll)mi[i-1][0]*27%mo1;
        mi[i][1]=(ll)mi[i-1][1]*27%mo2;
    }
    now1=now2=0;
    sum[0]=make_pair(0,0);
    fo(i,1,n){
        if (top&&s[sta[top]]==s[i]){
            (now1-=(ll)(s[sta[top]]-'a'+1)*mi[top-1][0]%mo1)%=mo1;
            (now1+=mo1)%=mo1;
            (now2-=(ll)(s[sta[top]]-'a'+1)*mi[top-1][1]%mo2)%=mo2;
            (now2+=mo2)%=mo2;
            top--;
        }
        else{
            (now1+=(ll)(s[i]-'a'+1)*mi[top][0]%mo1)%=mo1;
            (now2+=(ll)(s[i]-'a'+1)*mi[top][1]%mo2)%=mo2;
            sta[++top]=i;
        }
        sum[i]=make_pair(now1,now2);
    }
    if (top){
        printf("-1\n");
        return 0;
    }
    fo(i,1,n){
        //if (i){
            left[i]=p[s[i]-'a'][sum[i]];
            p[s[i]-'a'][sum[i]]=i;
        //}
    }
    solve(1,n);
    fo(i,1,n) printf("%c",ans[i]);
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 3
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值