CF IndiaHacks 2016 F Paper task 后缀数组

题目链接:http://codeforces.com/problemset/problem/653/F

大意是给出一个只包含'('和')'的括号串,求有多少不同的子串是合法的括号串

解法:对于每一个后缀,需要能够求出这个后缀有多少前缀是合法的括号串,这个可以用O(log n)复杂度的二分来解决。注意,二分的范围并不是整个后缀,因为如果将'('视作+1, ')'视作-1,则一个合法的括号串必须时刻不能小于0。所以可以在ST表上二分出合法范围,在这个范围内去统计有多少合法串(即'('与')'正负相消)。求出一个区间内有多少数字值为x可以对一个保存值和位置的pair数组排序后二分。

那么剩下的问题就是如何去重,由于height数组中记录的是排名相邻的两个后缀的最长公共前缀LCP,那么每一次统计只要根据这个信息减去相应重复统计的数量即可。

下面的代码中统计了两次num,是直接对上述思路的实现,其实是可以合并的。

  1 #include <iostream>
  2 #include <vector>
  3 #include <algorithm>
  4 #include <string>
  5 #include <string.h>
  6 #include <stdio.h>
  7 #include <math.h>
  8 #include <queue>
  9 #include <stack>
 10 #include <map>
 11 #include <set>
 12 
 13 using namespace std;
 14 
 15 
 16 const int N=500000+100;
 17 char s[N];
 18 int sum[N];
 19 vector< pair<int,int> >v;
 20 
 21 int preLog2[N];
 22 struct SparseTable {
 23     #define T int
 24     #define MAXN N
 25     static T MIN(T a,T b){return a<b?a:b;}
 26     static T MAX(T a,T b){return a>b?a:b;}
 27     SparseTable() {
 28         if (!preLog2[2]){
 29             preLog2[1]=0;
 30             for (int i=2;i<MAXN;i++)
 31                 preLog2[i]=preLog2[i>>1]+1;
 32         }
 33     }
 34     T dp[MAXN][21];
 35     T (*cmp) (T,T);
 36     void setMin(){cmp=MIN;}
 37     void setMax(){cmp=MAX;}
 38     void init(int n,T *val) {
 39         for (int i=0;i<n;i++)
 40             dp[i][0]=val[i];
 41         for (int j=1;(1<<j)<=n;j++) {
 42             int k=1<<(j-1);
 43             for (int i=0;i+k<n;i++)
 44                 dp[i][j]=cmp(dp[i][j-1],dp[i+k][j-1]);
 45         }
 46     }
 47     T query(int a,int b) {
 48         if (a>b) swap(a,b);
 49         int k=preLog2[b-a+1];
 50         return cmp(dp[a][k],dp[b-(1<<k)+1][k]);
 51     }
 52     #undef MAXN
 53     #undef T
 54 }tab;
 55 struct SuffixArray {
 56     int wa[N], wb[N], cnt[N], wv[N];
 57     int rk[N], height[N];
 58     int sa[N];
 59     bool cmp(int r[], int a, int b, int l) {
 60         return r[a] == r[b] && r[a+l] == r[b+l];
 61     }
 62     void calcSA(char r[], int n, int m) {
 63         int i, j, p, *x = wa, *y = wb;
 64         for (i = 0; i < m; ++i) cnt[i] = 0;
 65         for (i = 0; i < n; ++i) cnt[x[i]=r[i]]++;
 66         for (i = 1; i < m; ++i) cnt[i] += cnt[i-1];
 67         for (i = n-1; i >= 0; --i) sa[--cnt[x[i]]] = i;
 68         for (j = 1, p = 1; p < n; j *= 2, m = p) {
 69             for (p = 0, i = n - j; i < n; ++i) y[p++] = i;
 70             for (i = 0; i < n; ++i) if (sa[i] >= j) y[p++] = sa[i] - j;
 71             for (i = 0; i < n; ++i) wv[i] = x[y[i]];
 72             for (i = 0; i < m; ++i) cnt[i] = 0;
 73             for (i = 0; i < n; ++i) cnt[wv[i]]++;
 74             for (i = 1; i < m; ++i) cnt[i] += cnt[i-1];
 75             for (i = n-1; i >= 0; --i) sa[--cnt[wv[i]]] = y[i];
 76             for (swap(x, y), p = 1, x[sa[0]] = 0, i = 1; i < n; ++i)
 77                 x[sa[i]] = cmp(y, sa[i-1], sa[i], j) ? p-1 : p++;
 78         }
 79     }
 80     void calcHeight(char r[], int n) {
 81         int i, j, k = 0;
 82         for (i = 0; i <= n; ++i) rk[sa[i]] = i;
 83         for (i = 0; i < n; height[rk[i++]] = k)
 84             for (k?k--:0, j = sa[rk[i]-1]; r[i+k] == r[j+k]; k++);
 85     }
 86     int lcp(int a,int b,int len) {
 87         if (a==b) return len-a;
 88         int ra=rk[a],rb=rk[b];
 89         if (ra>rb) swap(ra,rb);
 90         return queryST(ra+1,rb);
 91     }
 92     int st[N][24];
 93     int preLog2[N];
 94     void initST(int n) {
 95         for (int i=1;i<=n; i++)
 96             st[i][0]=height[i];
 97         for (int j=1;(1<<j)<=n; j++) {
 98             int k=1<<(j-1);
 99             for (int i=1; i+k<=n; i++)
100                 st[i][j]=min(st[i][j-1],st[i+k][j-1]);
101         }
102         preLog2[1]=0;
103         for(int i=2;i<=n;i++){
104             preLog2[i]=preLog2[i>>1]+1;
105         }
106     }
107     int queryST(int a,int b) {
108         if (a>b) swap(a,b);
109         int dis=b-a+1;
110         int k=preLog2[dis];
111         return min(st[a][k],st[b-(1<<k)+1][k]);
112     }
113     void solve(int n) {
114         long long ret=0;
115         for (int i=1;i<=n;i++) {
116             if (s[sa[i]]==')') continue;
117             int l=sa[i],h=n-1,p=n;
118             while (l<=h) {
119                 int m=(l+h)>>1;
120                 if (tab.query(l,m)<sum[sa[i]]-1) {
121                     p=m;
122                     h=m-1;
123                 }
124                 else
125                     l=m+1;
126             }
127             p--;
128             int num=upper_bound(v.begin(),v.end(),make_pair(sum[sa[i]]-1,p))-lower_bound(v.begin(),v.end(),make_pair(sum[sa[i]]-1,sa[i]));
129             ret+=num;
130             int preR=min(sa[i]+height[i]-1,p);
131             num=upper_bound(v.begin(),v.end(),make_pair(sum[sa[i]]-1,preR))-lower_bound(v.begin(),v.end(),make_pair(sum[sa[i]]-1,sa[i]));
132             ret-=num;
133         }
134         printf("%I64d\n",ret);
135     }
136 }suf;
137 
138 int main() {
139     int n;
140     scanf("%d",&n);
141     scanf("%s",s);
142     sum[0]=(s[0]=='(')?1:-1;
143     for (int i=1;i<n;i++)
144         sum[i]=sum[i-1]+((s[i]=='(')?1:-1);
145     for (int i=0;i<n;i++) {
146         v.push_back(make_pair(sum[i],i));
147     }
148     sort(v.begin(),v.end());
149     tab.setMin();
150     tab.init(n,sum);
151     suf.calcSA(s,n+1,128);
152     suf.calcHeight(s,n);
153     suf.solve(n);
154     return 0;
155 }
View Code

 

posted @ 2016-03-22 23:16  活在夢裡  阅读(294)  评论(0编辑  收藏  举报