题意:给出一个有左右括号组成的字符串,现在问这个字符串中有多少个不重复的正规的括号序列。
思路:首先如果不考虑重复,我们可以求出有多少个包含重复的正规的括号序列,方法是
将左括号记为+1,右括号记为-1,处理出前缀和
对于以i开始的括号序列,所有满足j>i且sumv[j]=sumv[I-1] 且这段区间内的所有前缀和sumv[k]>=sumv[I-1]那么j就是一个满足条件的终止位置。
所以可以rmq预处理出区间最小值,再预处理出前缀和为s的下标集合
对于一个位置I,首先二分查找最远的点j,使得j满足条件对于任意I和j之间的元素k,满足sumv[k]>=sumv[I-1]
这个就是最远的可能满足的点,然后就可以在前缀和为sumv[I-1]的下标集合中二分一下,看看有多少下标在I和j之间。
现在问题有了升级,要求不重复的子串,方法使用后缀数组,处理出height数组,然后对于每个sa[I],它的可能初始区间变成了[sa[I]+height[I],n]然后在这个区间内二分最远的满足条件的坐标,剩下的处理方法就和上面一样了。
#include <bits/stdc++.h>
#define eps 1e-6
#define LL long long
#define pii pair<int, int>
#define pb push_back
#define mp make_pair
//#pragma comment(linker, "/STACK:1024000000,1024000000")
using namespace std;
const int N = 500010;
const int maxn = N<<1;
//const int INF = 0x3f3f3f3f;
struct SuffixArray {
int s[maxn]; /// 原始字符数组(最后一个字符应必须是0,而前面的字符必须非0)
int sa[maxn]; // 后缀数组,sa[0]一定是n-1,即最后一个字符
int rank[maxn]; // 名次数组
int height[maxn]; // height数组
int t[maxn], t2[maxn], c[maxn]; // 辅助数组
int n; // 字符个数
void clear() { n = 0; memset(sa, 0, sizeof(sa)); }
/// m为最大字符值加1。!!! 调用之前需设置好s和n
void build_sa(int m) {
int i, *x = t, *y = t2;
for(i = 0; i < m; i++) c[i] = 0;
for(i = 0; i < n; i++) c[x[i] = s[i]]++;
for(i = 1; i < m; i++) c[i] += c[i-1];
for(i = n-1; i >= 0; i--) sa[--c[x[i]]] = i;
for(int k = 1; k <= n; k <<= 1) {
int p = 0;
for(i = n-k; i < n; i++) y[p++] = i;
for(i = 0; i < n; i++) if(sa[i] >= k) y[p++] = sa[i]-k;
for(i = 0; i < m; i++) c[i] = 0;
for(i = 0; i < n; i++) c[x[y[i]]]++;
for(i = 0; i < m; i++) c[i] += c[i-1];
for(i = n-1; i >= 0; i--) sa[--c[x[y[i]]]] = y[i];
swap(x, y);
p = 1; x[sa[0]] = 0;
for(i = 1; i < n; i++)
x[sa[i]] = y[sa[i-1]]==y[sa[i]] && y[sa[i-1]+k]==y[sa[i]+k] ? p-1 : p++;
if(p >= n) break;
m = p;
}
}
void build_height() {
int i, j, k = 0;
for(i = 0; i < n; i++) rank[sa[i]] = i;
for(i = 0; i < n; i++) {
if(k) k--;
j = sa[rank[i]-1];
while(s[i+k] == s[j+k]) k++;
height[rank[i]] = k;
}
}
} sa;
int n;
int sumv[N];
vector<int> s[maxn];
int d[maxn][20];
void RMQ_init() {
for (int i = 1; i <= n; i++) d[i][0] = sumv[i];
for (int j = 1; (1<<j) <= n; j++) {
for (int i = 1; i+(1<<j)-1 <= n; i++)
d[i][j] = min(d[i][j-1], d[i+(1<<(j-1))][j-1]);
}
}
int RMQ(int L, int R) {
int k = 0;
while ((1<<(k+1)) <= R-L+1) k++;
return min(d[L][k], d[R-(1<<k)+1][k]);
}
int main()
{
//freopen("input.txt", "r", stdin);
scanf("%d", &n);
getchar();
sa.n = 0;
for (int i = 1; i <= n; i++) {
char c = getchar();
if (c == '(') {
sumv[i] = sumv[i-1] + 1;
sa.s[sa.n++] = 1;
}
else {
sumv[i] = sumv[i-1] - 1;
sa.s[sa.n++] = 2;
}
s[N+sumv[i]].push_back(i);
}
sa.sa[sa.n++] = 0;
sa.build_sa(5);
sa.build_height();
RMQ_init();
LL ans = 0;
for (int i = 1; i <= n; i++) {
int st = sa.sa[i] + 1;
if (sa.s[st-1] == 2) continue;
int ed = st + sa.height[i];
int L = ed, R = n;
while (L <= R) {
int mid = (L+R) >> 1;
if (RMQ(st, mid) >= sumv[st-1])
L = mid + 1;
else R = mid - 1;
}
ans += upper_bound(s[N+sumv[st-1]].begin(), s[N+sumv[st-1]].end(), R) - lower_bound(s[N+sumv[st-1]].begin(), s[N+sumv[st-1]].end(), ed);
//cout << sa.sa[i] << " " << ans << endl;
}
cout << ans << endl;
return 0;
}