zoukankan      html  css  js  c++  java
  • UOJ#219. 【NOI2016】优秀的拆分 [后缀数组 ST表]

    #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[N]; char s[N];
    namespace ST {
    	void build(int f[N][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[N], t1[N], t2[N], c[N], rnk[N], hei[N], f[N][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[N], g[N];
    inline void add(int *d, int l, int r) {d[l]++; d[r+1]--;}
    void solve(int L) { //printf("
    solve %d
    ",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]
    ", 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
    ",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
    ",i,f[i],g[i]);
    		for(int i=2; i<n; i++) ans += (ll)f[i]*g[i+1];
    		printf("%lld
    ", ans);
    	}
    }
    
    
  • 相关阅读:
    eslint 的 env 配置是干嘛使的?
    cookie httpOnly 打勾
    如何定制 antd 的样式(theme)
    剑指 Offer 66. 构建乘积数组
    剑指 Offer 65. 不用加减乘除做加法
    剑指 Offer 62. 圆圈中最后剩下的数字
    剑指 Offer 61. 扑克牌中的顺子
    剑指 Offer 59
    剑指 Offer 58
    剑指 Offer 58
  • 原文地址:https://www.cnblogs.com/candy99/p/6666621.html
Copyright © 2011-2022 走看看