UOJ#219. 【NOI2016】优秀的拆分 [后缀数组 ST表]
2017-04-04 21:47
381 查看
#219. 【NOI2016】优秀的拆分
题意:求有多少AABB样子的子串,拆分不同的同一个子串算多个一开始一直想直接求,并不方便
然后看了一眼Claris的题解的第一行就有思路了
如果分开,求\(f[i]\)以i结尾AA形式子串和\(g[i]\)以i开始AA形式子串 就可以套路了
使用常用技巧,枚举\(L=|A|\),AA子串一定覆盖了两个关键点,枚举更新就行了,对于区间加可以使用差分
其实这道题很好拿95分啊,\(O(n^2)\)用哈希判断就行了
#include <iostream> #include <cstdio> #include <cstring> #include <algorithm> #include <cmath> using namespace std; const int N=3e4+5; typedef long long ll; inline int read(){ char c=getchar();int x=0,f=1; while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();} while(c>='0'&&c<='9'){x=x*10+c-'0';c=getchar();} return x*f; } int n, Log ; char s ; namespace ST { void build(int f [16], int *a) { for(int i=1; i<=n; i++) f[i][0]=a[i]; for(int j=1; j<15; j++) for(int i=1; i+(1<<j)-1<=n; i++) f[i][j] = min(f[i][j-1], f[i+(1<<(j-1))][j-1]); } } struct SA { int sa , t1 , t2 , c , rnk , hei , f [16]; inline bool cmp(int *r, int a, int b, int j) { return a+j<=n && b+j<=n && r[a]==r[b] && r[a+j]==r[b+j]; } void build(char *s, int m) { int *r=t1, *k=t2; for(int i=0; i<=m; i++) c[i]=0; for(int i=1; i<=n; i++) c[r[i]=s[i]]++; for(int i=1; i<=m; i++) c[i] += c[i-1]; for(int i=n; i>=1; i--) sa[ c[r[i]]-- ]=i; for(int j=1; j<=n; j<<=1) { int p=0; for(int i=n-j+1; i<=n; i++) k[++p]=i; for(int i=1; i<=n; i++) if(sa[i]>j) k[++p]=sa[i]-j; for(int i=0; i<=m; i++) c[i]=0; for(int i=1; i<=n; i++) c[r[k[i]]]++; for(int i=1; i<=m; i++) c[i] += c[i-1]; for(int i=n; i>=1; i--) sa[ c[r[k[i]]]-- ]=k[i]; swap(r, k); p=0; r[sa[1]]=++p; for(int i=2; i<=n; i++) r[sa[i]] = cmp(k, sa[i], sa[i-1], j) ? p : ++p; if(p>=n) break; m=p; } int now=0; for(int i=1; i<=n; i++) rnk[sa[i]]=i; for(int i=1; i<=n; i++) { if(now) now--; if(rnk[i]==1) continue; int j=sa[rnk[i]-1]; while(i+now<=n && j+now<=n && s[i+now]==s[j+now]) now++; hei[rnk[i]]=now; } ST::build(f, hei); } int lcp(int x, int y) { x=rnk[x], y=rnk[y]; if(x>y) swap(x, y); x++; int t=Log[y-x+1]; return min(f[x][t], f[y-(1<<t)+1][t]); } }a, b; inline int lcp(int x, int y) {return a.lcp(x, y);} inline int lcs(int x, int y) {return b.lcp(n-x+1, n-y+1);} int f , g ; inline void add(int *d, int l, int r) {d[l]++; d[r+1]--;} void solve(int L) { //printf("\nsolve %d\n",L); for(int i=1; i+L<=n; i+=L) { int l = i - lcs(i, i+L) + 1, r = i + L + lcp(i, i+L) - 1; l = max(l, i-L+1); r = min(r, i+L+L-1); l = max(l, 1); r = min(r, n); //printf("key %d %d [%d, %d]\n", i, i+L, l, r); if(r-l+1 < 2*L) continue; add(f, l+2*L-1, r); add(g, l, r-2*L+1); } } int main() { freopen("in","r",stdin); Log[1]=0; for(int i=2; i<N; i++) Log[i] = Log[i>>1]+1; int T=read(); while(T--) { scanf("%s", s+1); n=strlen(s+1); a.build(s, 260); reverse(s+1, s+1+n); b.build(s, 260); reverse(s+1, s+1+n); for(int i=1; i<=n; i++) f[i]=g[i]=0; //for(int i=1; i<=n; i++) for(int j=i; j<=n; j++) printf("lcs %d %d %d\n",i,j,lcs(i,j)); for(int i=1; i<=n; i++) solve(i); ll ans=0; for(int i=1; i<=n; i++) f[i]+=f[i-1], g[i]+=g[i-1];// printf("look %d %d %d\n",i,f[i],g[i]); for(int i=2; i<n; i++) ans += (ll)f[i]*g[i+1]; printf("%lld\n", ans); } }
相关文章推荐
- NOI2016 优秀的拆分 [后缀数组]
- 【后缀数组】[NOI2016]优秀的拆分
- [BZOJ4650][NOI2016]优秀的拆分-后缀数组
- bzoj 4650: [Noi2016]优秀的拆分 后缀数组
- BZOJ4650 [NOI2016]优秀的拆分 【后缀数组】
- [后缀数组 枚举 字符串分段] BZOJ 4650 [Noi2016]优秀的拆分
- BZOJ4650/UOJ219 [Noi2016]优秀的拆分
- NOI2016 优秀的拆分 后缀数组
- UOJ #219 [NOI2016 D1T1] 优秀的拆分 [95分]
- [后缀数组] BZOJ4650: [Noi2016] 优秀的拆分
- 【UOJ 219】【BZOJ 4650】【NOI 2016】优秀的拆分(后缀数组)
- 洛谷P1117:[NOI2016]优秀的拆分(后缀自动机)
- 【BZOJ4650&UOJ219】优秀的拆分(二分,hash)
- NOI 2016 优秀的拆分 后缀数组
- 【BZOJ4199&UOJ131】品酒大会(后缀数组,并查集)
- BZOJ4650: [Noi2016]优秀的拆分
- Uoj #131. 【NOI2015】品酒大会 后缀数组,并查集
- NOI2016 优秀的拆分(图解)
- BZOJ4199 [Noi2015]品酒大会 【后缀数组 + 单调栈 + ST表】
- BZOJ4650 : [NOI2016]优秀的拆分