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);
    	}
    }
    
    
  • 相关阅读:
    MIME协议(一) -- RFC822邮件格式
    生活
    面向对象设计的SOLID原则、迪米特法则
    策略模式
    asp.net Json序列化
    简单工厂模式
    CSS响应式布局实例
    CSS响应式网站开发
    mysql 记录根据日期字段倒序输出
    IE下JS保存图片
  • 原文地址:https://www.cnblogs.com/candy99/p/6666621.html
Copyright © 2011-2022 走看看