2015 Multi-University Training Contest 6 (hdu 5357 - Easy Sequence)栈的应用

本文介绍了一种算法,用于解决给定由括号组成的字符串时如何快速计算每个字符所在有效子串的数量问题。该算法利用栈来确定括号的匹配关系,并通过计算同级括号对数量得出每个位置的有效子串数。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Easy Sequence
Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 131072/131072 K (Java/Others)
Total Submission(s): 643 Accepted Submission(s): 185

Problem Description
soda has a string containing only two characters – ‘(’ and ‘)’. For every character in the string, soda wants to know the number of valid substrings which contain that character.

Note:
An empty string is valid. If S is valid, (S) is valid. If U,V are valid, UV is valid.

Input
There are multiple test cases. The first line of input contains an integer T, indicating the number of test cases. For each test case:

A string s consisting of ‘(’ or ‘)’ (1|s|106).

Output
For each test case, output an integer m=∑i=1|s|(i⋅ansi mod 1000000007), where ansi is the number of valid substrings which contain i-th character.

Sample Input

2
()()
((()))

Sample Output

20
42
Hint

For the second case, ans={1,2,3,3,2,1}, then m=1⋅1+2⋅2+3⋅3+4⋅3+5⋅2+6⋅1=42
其覆盖方案分为两种类型:

例子:”…()(…()()()…)()…” (根据合法序列是否覆盖了“包括了它的最小的括号对”进行分类)

第一部分:倘若它的合法序列覆盖了整个括号对,那么这一部分的方案数等于括号对的总方案数
第二部分:倘若它的合法序列在括号对内,那么这种合法序列一定覆盖了其两边有与其“同级别"(即互不包含)的括号对,设左边有a个,右边有b个,则这一部分的方案数为 (a + 1) * (b + 1)

合并在一起就求出了每个位置的方案数

做法:计算出第 i 个括号的匹配括号位置 match[i],包含它的括号对的左括号位置 pre[i],以及:

对于左括号,其右边的“同级别”的括号个数(包括自己)forward[i] = forward[match[i]+1] + 1
对于右括号,其左边的“同级别”的括号个数(包括自己)backward[i] = backward[match[i]-1] + 1

其中,match[i] 和 pre[i] 可以对序列用栈扫一遍求出,具体做法是:遇到左括号压进栈,遇到右括号就弹栈,之前的栈顶元素与当前元素相互 match,现在的栈顶元素成为当前元素及其 match 的 pre。

最后,ans[i] = ans[match[i]] = ans[pre[i]] + forward[i] * backward[match[i]]

#include<iostream>
#include<cstdio>
#include<string>
#include<cstring>
#include<vector>
#include<cmath>
#include<queue>
#include<stack>
#include<map>
#include<set>
#include<algorithm>
using namespace std;
typedef long long LL;
const int maxn=1000100;
const int maxm=1010;
const int MOD=1000000007;
const int INF=0x3f3f3f3f;
char s[maxn];
int N;
int match[maxn],pre[maxn],R[maxn],L[maxn];
LL ans[maxn];
int st[maxn];
void solve(){
    for(int i=1;i<=N;i++){
        s[i]=(s[i]==')');
    }
    memset(match,0,sizeof(int)*(N+2));
    memset(pre,0,sizeof(int)*(N+2));
    memset(L,0,sizeof(int)*(N+2));
    memset(R,0,sizeof(int)*(N+2));
    int top=0;
    for(int i=1;i<=N;i++){
        if(s[i]==0){
            st[++top]=i;
        } else if(top){
            match[st[top]]=i;
            match[i]=st[top];
            --top;
            if(top){
                pre[match[i]]=st[top];
            }
        }
    }
    for(int i=N;i>0;i--){
        if(s[i]==0&&match[i]){
            R[i]=R[match[i]+1]+1;
        }
    }
    for(int i=1;i<=N;i++){
        if(s[i]==1&&match[i]){
            L[i]=L[match[i]-1]+1;
        }
    }
    memset(ans,0,sizeof(LL)*(N+2));
    LL res=0;
    for(int i=1;i<=N;i++){
        if(s[i]==0&&match[i]){
            ans[i]=ans[match[i]]=(ans[pre[i]]+1LL*R[i]*L[match[i]]%MOD)%MOD;
        }
        res+=1LL*i*ans[i]%MOD;
    }
    printf("%I64d\n",res);
}
int main(){
    int T;
    scanf("%d",&T);
    while(T--){
        scanf("%s",s+1);
        N=strlen(s+1);
        solve();
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值