题目:

这个题面是真的有毒,就不发题面了。

题意是:给一串字符,求它的子序列里面有多少个满足括号序列,且相互匹配的左括号和右括号必须是同一个字符。

 

 

 

分析:

30分暴力:

枚举每一个子序列,扫一遍判断是否满足括号序列。

判断方法:遇到相同的就弹出,最后栈为空。

O(n^3)

60分暴力:

基于30分做法的优化:只枚举子序列的左端点x,判断的时候从x~n,只要中间某一步为空,就将答案++。

这样就可以遍历整个序列。

O(n^2)

满分做法:

基于60分做法的优化:直接扫一遍(按照进出栈的原则),求出在每一个点栈中所有元素的状态。

X表示栈为空,如下图:

把相同状态的提出来,会发现其中间夹着的区间是一段合法的括号序列。

所以只需将每一个点的状态(用hash记录)排一下序,假设有x个相同的数,ans+=(x-1)*x/2。(即这x个数两两组合得到的贡献)

为什么是对的呢?

状态相同的原因是中间经历了进栈又出栈的过程,而这个过程后,又将栈中的状态还原了,也就是说,这一段子序列是满足括号序的。

O(n)

(思想很巧妙)

#include<bits/stdc++.h>
using namespace std;
#define ri register int
#define N 1000005
#define ll long long
#define usll unsigned long long
#define base 131
#define mod 998244353
char s[N],col[N];//col记录栈中元素 
usll stk[N],ha[N];//记录目前栈中的hash值 记录每一个点的状态hash值 
int main()
{
    freopen("B.in","r",stdin);
    freopen("B.out","w",stdout);
    scanf("%s",s+1);
    int len=strlen(s+1);
    int top=0,cnt=0;
    ha[++cnt]=stk[++top]=s[1]-'a'+1; col[top]=s[1];
    for(ri i=2;i<=len;++i){
        if(s[i]==col[top] && top>0) top--,ha[++cnt]=stk[top];
        else ha[++cnt]=stk[top]*base+s[i]-'a'+1,col[++top]=s[i],stk[top]=stk[top-1]*base+s[i]-'a'+1;
    }
    sort(ha,ha+1+cnt);
    ll ans=0;
    int now=0;
    while(now<=cnt){//跳连续区间统计答案 
        ll tmp=1;
        while(ha[now+tmp]==ha[now]) tmp++;
        tmp--;
        ans=(ans+tmp*(tmp+1)/2)%mod;
        if(tmp) now+=tmp;
        else now++;
    }
    printf("%lld\n",ans);
}
/*
aababba
aabaab
aabbcc
aabbaabbaabbaabbaabbaabb
*/
View Code

 

posted on 2019-10-06 17:28  rua-rua-rua  阅读(151)  评论(0编辑  收藏  举报