CF 653F 后缀数组

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

解法:对于每一个后缀,需要能够求出这个后缀有多少前缀是合法的括号串,这个可以用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 }

时间: 2024-06-21 18:54:34

CF 653F 后缀数组的相关文章

CF 427D 后缀数组

大意是寻找两个字符串中最短的公共子串,要求子串在两个串中都是唯一的. 造一个S#T的串,做后缀数组,从小到大枚举子串长度在height数组中扫描,如果某一个组中来自两个串的数量分别为1,就找到了答案. 1 #include <iostream> 2 #include <vector> 3 #include <algorithm> 4 #include <string> 5 #include <string.h> 6 #include <st

CF(427D-Match &amp; Catch)后缀数组应用

题意:给两个字符串,求一个最短的子串.使得这个子串在两个字符串中出现的次数都等于1.出现的定义为:可以重叠的出现. 解法:后缀数组的应用.从小枚举长度.如果一个长度len合法的话:则一定存在这个样的sa[i]排名.sa[i]与s[i+1]的公共前缀长度大于等于len,且sa[i]与[i-1]的公共前缀长度小于len,同时sa[i+1]与[i+2]的公共前缀长度小于len,同时保证sa[i]与sa[i+1]在两个串中.Judge函数就是技巧性地实现了这些判断. 代码: #include<iostr

CF 1320D Reachable Strings 后缀数组

确定性解法:后缀数组 众所周知 hash 由于不能完全避免冲突常常会被 cf hacker 卡掉,此题有更稳定的解法 考虑将原串进行简化,由于只有连续的 1 会造成不确定的匹配,可以只对 0 建立一个新串 对于一段连续的 1,后面的 0 建立新字符,如果有奇数个 1 就建立 1,偶数个就建立 0 例如: 1110 -> 1,110 -> 0,11011 -> 0 由于最后的 1 是没有后导 0 的就不计入新串 然后新串的 lcp 就可以对应到原串的对应位置,新串 lcp 匹配就等价于原串

CF 504E Misha and LCP on Tree(树链剖分+后缀数组)

题目链接:http://codeforces.com/problemset/problem/504/E 题意:给出一棵树,每个结点上有一个字母.每个询问给出两个路径,问这两个路径的串的最长公共前缀. 思路:树链剖分,记录每条链的串,正反都记,组成一个大串.记录每条链对应的串在大串中的位置.然后对大串求后缀数组.最后询问就是在一些链上的查询. 树链剖分总是那么优秀.. const int N=600005; int next[N],node[N],head[N],e; void add(int u

CF(427D-Match &amp;amp; Catch)后缀数组应用

题意:给两个字符串,求一个最短的子串.使得这个子串在两个字符串中出现的次数都等于1.出现的定义为:能够重叠的出现. 解法:后缀数组的应用.从小枚举长度.假设一个长度len合法的话:则一定存在这个样的sa[i]排名.sa[i]与s[i+1]的公共前缀长度大于等于len,且sa[i]与[i-1]的公共前缀长度小于len,同一时候sa[i+1]与[i+2]的公共前缀长度小于len,同一时候保证sa[i]与sa[i+1]在两个串中.Judge函数就是技巧性地实现了这些推断. 代码: #include<i

SPOJ 705 Distinct Substrings(后缀数组)

[题目链接] http://www.spoj.com/problems/SUBST1/ [题目大意] 给出一个串,求出不相同的子串的个数. [题解] 对原串做一遍后缀数组,按照后缀的名次进行遍历, 每个后缀对答案的贡献为n-sa[i]+1-h[i], 因为排名相邻的后缀一定是公共前缀最长的, 那么就可以有效地通过LCP去除重复计算的子串. [代码] #include <cstdio> #include <cstring> #include <algorithm> usi

hdu5769--Substring(后缀数组)

题意:求含有某个字母的某个字符串的不同子串的个数 题解:后缀数组,记录每个位置距离需要出现的字母的距离就可以了.因为不太了解后缀模版卡了一会,还是很简单的. 记住sa和height数组都是1-n的下标. //后缀数组 #include <stdio.h> #include <cstring> #include <iostream> #include <algorithm> using namespace std; typedef long long ll;

hdu 3518 Boring counting 后缀数组LCP

题目链接 题意:给定长度为n(n <= 1000)的只含小写字母的字符串,问字符串子串不重叠出现最少两次的不同子串个数; input: aaaa ababcabb aaaaaa # output 2 3 3 思路:套用后缀数组求解出sa数组和height数组,之后枚举后缀的公共前缀长度i,由于不能重叠,所以计数的是相邻height不满足LCP >= i的. 写写对后缀数组倍增算法的理解: 1.如果要sa数组对应的值也是1~n就需要在最后加上一个最小的且不出现的字符'#',里面y[]是利用sa数

【tyvj1860】后缀数组

描述 我们定义一个字符串的后缀suffix(i)表示从s[i]到s[length(s)]这段子串.后缀数组(Suffix array)SA[i]中存放着一个排列,满足suffix(sa[i])<suffix(sa[i+1]) 按照字典序方式比较定义height[i]表示suffix(sa[i])与suffix(sa[i-1])之间的最长公共前缀长度,其中height[1]=0你的任务就是求出SA和height这两个数组.字符串长度<=200000 输入格式 一行,为描述中的字符串(仅会出现小写