已经没有什么好害怕的了
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();
}
}