CodeForces 653F Paper task(rmq+二分+后缀数组)

题意:给出一个有左右括号组成的字符串,现在问这个字符串中有多少个不重复的正规的括号序列。

思路:首先如果不考虑重复,我们可以求出有多少个包含重复的正规的括号序列,方法是

将左括号记为+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;
}
















  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值