Codeforces 990C (模拟+组合数学)

题面:

传送门

分析:

此题 O ( n 2 l ) 模拟肯定是会超时的(l为所有字符串总长)
我们想到对字符串进行一定的预处理,可以快速计算匹配
我们设每一个(的值为1,)的值为-1,规定
若只有)括号多了x个,则l[i]=r[i]=-x<0
若只有(括号多了x个,则l[i]=r[i]=x>0
那么如何求l[i],r[i]的值呢?
从左到右扫描字符串,用一个变量cnt,统计和
cnt的最小值=l[i],最终的值=r[i]
若(和)都有多余,则必须接两个字符串,不符合条件,所以计算时应该直接跳过这些字符串,不用考虑

要理解这句话,请看下面的模拟过程
)()
字符串下标(0开始) 0 1 2
cnt值 -1 0 -1
l[i]=-1,说明左边需要一个(括号来匹配 (>=0即不需要)
r[i]=-1,说明右边不需要(<=0即不需要),而左边需要一个(括号

我们以all[i]来hash,哈希表的第 x位存储了所有r[i]=x的字符串的l[i]
但要注意的是,由于负数的原因,数组下标要人为加上一个数addv来存储

为了防止一对字符串被算两次,我们规定字符串只能接在右边,我们枚举字符串时要先保证l[i]>=0才能接,而l[i]<0的只能接在其他字符串右边
对于一个l[i]>=0的字符串i,我们寻找接在它右侧能匹配的字符串,所以我们在哈希表的 r [ i ] 位置寻找l[i]值为 r [ i ] 的字符串

比如:()(的l[i]=0,r[i]=1,我们则要寻找r[i]=l[i]=-1,即只多了一个)括号的字符串

用二分查找统计值为 r [ i ] 的字符串的个数,答案+=值为 r [ i ] 的字符串的个数
预处理时间复杂度 O ( l ) (l为所有字符串总长)
二分查找时间复杂度 O ( n l o g 2 l )

代码:

#include<iostream>
#include<cstdio>
#include<cstring>
#include<vector> 
#include<algorithm>
#define addv 300005
#define maxn 2*addv
using namespace std;
int n;
char str[maxn];
int cnt;
int l[maxn];
int r[maxn];
vector<int>table[maxn];
int main(){
    scanf("%d",&n);
    for(int i=1;i<=n;i++){
        scanf("%s",str);
        int len=strlen(str);
        l[i]=maxn;
        cnt=0;
        for(int j=0;j<len;j++){//像分析中一样求l[i],r[i]
            if(str[j]=='(') cnt++;
            else cnt--;
            l[i]=min(l[i],cnt);
        }
        r[i]=cnt;
        table[r[i]+addv].push_back(l[i]);//hash,为了防止负数人为加上addv,addv就相当于新的零点
    }
    for(int i=1;i<maxn;i++) if(table[i].size()!=0) sort(table[i].begin(),table[i].end());//排序,为了二分查找
    long long ans=0;
    for(int i=1;i<=n;i++){
        if(l[i]>=0){
            int tmp=addv-r[i];//即-r[i]
            ans+=table[tmp].end()-lower_bound(table[tmp].begin(),table[tmp].end(),-r[i]);//lower_bound返回第一个>=x的位置,用结尾去-它,可求出l[j]=r[j]=-r[i]的j的个数
        }
    }
    printf("%I64d\n",ans);
} 

猜你喜欢

转载自blog.csdn.net/oier_forever/article/details/80655488
今日推荐