已经没有什么好害怕的了

Description

小Y 最近开始学习算法姿势,但是因为小R 非常BB,给了她很多B6 题,所以她觉得自己已经没有什么前途了。于是小R 给了她一些稍微简单的题,让她觉得已经没有什么好害怕的了,其中一道是这样的:
给定一个长度为n 只包含左括号和右括号的序列,现在小R 想要知道经过每一个位置的合法子串有多少个。
空串是一个合法的串,如果A 和B 都是合法的串,那么(A) 和AB 都是合法的串。

Input

第一行输入一个正整数T 表示数据组数。接下来T 行每行一个字符串。

Output

对于每组数据,输出一个整数表示答案,令ansi 为经过第i 个位置的子串个数,那么你需要输出(注意是先求余再求和)

Sample Input

1
()()

Sample Output

20
样例解释:
ans 数组为{2,2,2,2},所以输出20。

Data Constraint

对于10% 的数据,n<=100
对于30% 的数据,n <= 1000
对于60% 的数据,n <= 5 <= 10^4
对于100% 的数据,n <= 10^6,1 <= T<= 10

.
.
.
.
.
.
分析
栈+差分约束
用栈处理对应的括号
把整个字符串拆成几段(跳过没用的括号),然后计算即可
对于每一段序列,我们从大到小处理,即从整体到局部
再用差分约束的思想求出每个点所被经过的合法的括号序列的个数

.
.
.
.
.
程序:

#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;

int n,a[1000010],l[1000010],r[1000010],f[1000010],next[1000010],last[1000010];
char zf[1000010];
long long ans,t[1000010];

void work() 
{
    memset(l,0,sizeof(l));
    memset(r,0,sizeof(r));
    memset(t,0,sizeof(t));
    
    for (int i=n+1;i>=1;i--) 
	{
        r[i]++;
        r[last[i]]+=r[i];
    }
    
    for (int i=1;i<=n;i++) 
	{
        l[i]--;
        l[next[i]]+=l[i];
    }
    for (int i=1;i<=n;i++) 
		t[i]=r[i]+l[i];
    for (int i=1;i<=n;i++) 
		t[i]+=t[i-1];
    long long ans=0;
    for (int i=1;i<=n;i++) 
		ans=(long long)ans+(long long)i*t[i]%1000000007;
    printf("%lld\n",ans);
}

int main() 
{
    int t;
    scanf("%d",&t);
    while (t--) 
	{
        scanf("%s",zf+1);
    	n=strlen(zf+1);
        memset(f,0,sizeof(f));
    	memset(last,0,sizeof(last));
    	memset(next,0,sizeof(next));
    	int head=0;
    	for (int i=1;i<=n;i++) 
			if (zf[i]=='(') f[++head]=i; else 
			if (head!=0)
			{
				next[f[head]]=i+1;
				last[i+1]=f[head--];
			}
        work();
    }
}
posted @ 2019-01-21 20:01  银叶草  阅读(236)  评论(0编辑  收藏  举报
Live2D