• CodeForces


    ( ext{Description})

    传送门

    ( ext{Solution})

    容易发现如果算出长度为 (L) 的公共前缀为 (x)(A,B,C) 个数为 ((cnt_1,cnt_2,cnt_3)),那么长度为 (len) 小于 (L) 的公共前缀为 ”(x) 的前 (len) 位” 的 (A,B,C) 个数至少有 ((cnt_1,cnt_2,cnt_3))

    我们将 (h) 从大到小枚举,将新的满足条件的共同前缀合并。

    比如有:

    ( ext{woshizhu})

    ( ext{woshixiannv})

    本来在 (L>5) 时这两个串没有关联,当 (L=5) 时就可以合并。

    ( ext{Code})

    #include <cstdio>
    
    #define rep(i,_l,_r) for(register signed i=(_l),_end=(_r);i<=_end;++i)
    #define fep(i,_l,_r) for(register signed i=(_l),_end=(_r);i>=_end;--i)
    #define erep(i,u) for(signed i=head[u],v=to[i];i;i=nxt[i],v=to[i])
    #define efep(i,u) for(signed i=Head[u],v=to[i];i;i=nxt[i],v=to[i])
    #define print(x,y) write(x),putchar(y)
    
    template <class T> inline T read(const T sample) {
        T x=0; int f=1; char s;
        while((s=getchar())>'9'||s<'0') if(s=='-') f=-1;
        while(s>='0'&&s<='9') x=(x<<1)+(x<<3)+(s^48),s=getchar();
        return x*f;
    }
    template <class T> inline void write(const T x) {
        if(x<0) return (void) (putchar('-'),write(-x));
        if(x>9) write(x/10);
        putchar(x%10^48);
    }
    template <class T> inline T Max(const T x,const T y) {if(x>y) return x; return y;}
    template <class T> inline T Min(const T x,const T y) {if(x<y) return x; return y;}
    template <class T> inline T fab(const T x) {return x>0?x:-x;}
    template <class T> inline T gcd(const T x,const T y) {return y?gcd(y,x%y):x;}
    template <class T> inline T lcm(const T x,const T y) {return x/gcd(x,y)*y;}
    template <class T> inline T Swap(T &x,T &y) {x^=y^=x^=y;}
    
    #include <cstring>
    #include <iostream>
    #include <algorithm>
    using namespace std;
    
    const int mod=1e9+7,maxn=3e5+5;
    
    int ans[maxn],ret,n,m=122,siz,minn,ori[maxn],op[maxn],x[maxn],y[maxn],sa[maxn],c[maxn],num,rk[maxn],h[maxn],f[maxn],dp[maxn][4],p[maxn];
    char s[maxn];
    
    void Read() {
    	scanf("%s",s+1); n=siz=minn=strlen(s+1);
    	rep(i,1,siz) ori[i]=s[i],op[i]=0;
    	ori[++n]=1; op[n]=-1;
    	scanf("%s",s+1); siz=strlen(s+1); minn=Min(minn,siz);
    	rep(i,n+1,n+siz) ori[i]=s[i-n],op[i]=1;
    	ori[n=n+siz+1]=2,op[n]=-1;
    	scanf("%s",s+1); siz=strlen(s+1); minn=Min(minn,siz);
    	rep(i,n+1,n+siz) ori[i]=s[i-n],op[i]=2;
    	n+=siz;
    }
    
    void Suffix() {
    	rep(i,1,n) ++c[x[i]=ori[i]];
    	rep(i,2,m) c[i]+=c[i-1];
    	rep(i,1,n) sa[c[x[i]]--]=i;
    	for(int k=1;k<=n;k<<=1) {
    		num=0;
    		rep(i,n-k+1,n) y[++num]=i;
    		rep(i,1,n) if(sa[i]>k) y[++num]=sa[i]-k;
    		rep(i,1,m) c[i]=0;
    		rep(i,1,n) ++c[x[i]];
    		rep(i,2,m) c[i]+=c[i-1];
    		fep(i,n,1) sa[c[x[y[i]]]--]=y[i],y[i]=0;
    		swap(x,y);
    		x[sa[1]]=num=1;
    		rep(i,2,n)
    			x[sa[i]]=(y[sa[i]]==y[sa[i-1]]&&y[sa[i]+k]==y[sa[i-1]+k])?num:++num;
    		m=num;
    	}
    }
    
    void LCP() {
    	int k=0;
    	rep(i,1,n) rk[sa[i]]=i;
    	rep(i,1,n) {
    		if(rk[i]==1) continue;
    		if(k) --k;
    		int j=sa[rk[i]-1];
    		while(j+k<=n&&i+k<=n&&ori[j+k]==ori[i+k]) ++k;
    		h[rk[i]]=k;
    	}
    }
    
    void init() {
    	rep(i,1,n) f[i]=p[i]=i;
    }
    
    bool cmp(int x,int y) {
    	return h[x]>h[y];
    }
    
    int Find(int x) {return x==f[x]?x:f[x]=Find(f[x]);}
    
    void Merge(int x,int y) {
    	if(x==y) return;
    	ret=(ret-1ll*dp[x][0]*dp[x][1]%mod*dp[x][2]%mod+mod)%mod; // 减去之前的贡献,不然会算重,因为我们是严格要求长度为 L
    	ret=(ret-1ll*dp[y][0]*dp[y][1]%mod*dp[y][2]%mod+mod)%mod;
    	rep(i,0,2) dp[x][i]=(dp[x][i]+dp[y][i])%mod;
    	ret=(ret+1ll*dp[x][0]*dp[x][1]%mod*dp[x][2]%mod+mod)%mod;
    	f[y]=x;
    }
    
    int main() {
    	Read(); Suffix(); LCP();
    	init(); sort(p+1,p+n+1,cmp);
    	rep(i,1,n) if(~op[i]) dp[i][op[i]]=1;
    	int j=1;
    	fep(i,minn,1) {
    		while(j<=n&&h[p[j]]>=i) {
    			Merge(Find(sa[p[j]]),Find(sa[p[j]-1]));
    			++j;
    		}
    		ans[i]=ret;
    	}
    	rep(i,1,minn) print(ans[i],' '); puts("");
    	return 0;
    }
    

    ( ext{Old Version})

    感觉题目越来越不会做了怎么办(QWQ)。如今只有康题解辽。

    你能想象这道题用并查集来优化吗?

    (Solution)

    枚举特别慢,我们就按顺序枚举。从大到小保证符合大的要求一定符合小的要求。

    我们相当于做一个后缀和。每次加入新的满足的情况数。

    巧妙的是并查集。因为无法确定下一次,所以只能用一坨的和来做。这就符合并查集的性质。

    我的码风真好看。(自恋五秒钟

    #include <cstdio>
    #include <cstdlib>
    #include <cstring>
    #include <iostream>
    #include <algorithm>
    using namespace std;
    typedef long long ll;
    
    const int N = 3e5 + 8;
    const ll mod = 1e9 + 7;
    
    int ans[N], f[N], p[N], id[N], lg[N], len, rmq[N][20], n, m, h[N], tax[N], SA[N], tp[N], rk[N], a[N];
    ll dp[N][3], tmp;
    
    int read() {
    	int x = 0, f = 1; char S;
    	while((S = getchar()) > '9' || S < '0') {
    		if(S == '-') f = -1;
    		if(S == EOF) exit(0);
    	}
    	while(S <= '9' && S >= '0') {
    		x = (x << 1) + (x << 3) + (S ^ 48);
    		S = getchar();
    	}
    	return x * f;
    }
    
    bool cmp(const int x, const int y, const int d) {return tp[x] == tp[y] && tp[x + d] == tp[y + d];}
    
    void Sort() {
        for(int i = 0; i <= m; ++ i) tax[i] = 0;
        for(int i = 1; i <= n; ++ i) ++ tax[rk[tp[i]]];
        for(int i = 1; i <= m; ++ i) tax[i] += tax[i - 1];
        for(int i = n; i >= 1; -- i) SA[tax[rk[tp[i]]] --] = tp[i];
    }
    
    void init() {
        char ch[N];
        scanf("%s", ch); int siz = strlen(ch); len = siz;
        for(int i = 0; i < siz; ++ i) a[++ n] = ch[i], id[n] = 1;
        a[++ n] = 1; id[n] = -1;
        scanf("%s", ch); siz = strlen(ch); len = min(len, siz);
        for(int i = 0; i < siz; ++ i) a[++ n] = ch[i], id[n] = 2;
        a[++ n] = 2; id[n] = -1;
        scanf("%s", ch); siz = strlen(ch); len = min(len, siz);
        for(int i = 0; i < siz; ++ i) a[++ n] = ch[i], id[n] = 3;
        a[++ n] = 3; id[n] = -1;
        m = 122;
    }
    
    void Suffix() {
        init();
        for(int i = 1; i <= n; ++ i) rk[i] = a[i], tp[i] = i;
        Sort();
        for(int w = 1, p = 1, i; p < n; m = p, w <<= 1) {
            for(p = 0, i = n - w + 1; i <= n; ++ i) tp[++ p] = i;
            for(i = 1; i <= n; ++ i) if(SA[i] > w) tp[++ p] = SA[i] - w;
            Sort(); swap(rk, tp); rk[SA[1]] = p = 1;
            for(i = 2; i <= n; ++ i) rk[SA[i]] = cmp(SA[i], SA[i - 1], w) ? p : ++ p;
        }
        int j, k = 0;
        for(int i = 1; i <= n; h[rk[i ++]] = k)
            for(k = k ? k - 1 : k, j = SA[rk[i] - 1]; a[i + k] == a[j + k]; ++ k);
        for(int i = 2; i <= n; ++ i) lg[i] = lg[i >> 1] + 1;
        for(int i = 1; i <= n; ++ i) rmq[i][0] = h[i];
        for(int j = 1; (1 << j) <= n; ++ j)
            for(int i = 1; i + (1 << j) - 1 <= n; ++ i)
                rmq[i][j] = min(rmq[i][j - 1], rmq[i + (1 << j - 1)][j - 1]);
    }
    
    int lcp(const int x, const int y) {
        int l = rk[x], r = rk[y];
        if(l == r) return n - SA[l] + 1;
        if(l > r) swap(l, r);
        int t = lg[r - l];
        return min(rmq[l + 1][t], rmq[r - (1 << t) + 1][t]);
    }
    
    void makeSet() {
        for(int i = 1; i <= n; ++ i) p[i] = i, f[i] = i;
    }
    
    int findSet(const int x) {
        return x == f[x] ? x : f[x] = findSet(f[x]);
    }
    
    void sub(ll &x, const int y) {
        x = (x - y + mod) % mod;
    }
    
    void unionSet(const int x, const int y) {
        sub(tmp, dp[x][0] * dp[x][1] % mod * dp[x][2] % mod);
        sub(tmp, dp[y][0] * dp[y][1] % mod * dp[y][2] % mod);
        for(int i = 0; i < 3; ++ i) (dp[x][i] += dp[y][i]) %= mod;
        (tmp += dp[x][0] * dp[x][1] % mod * dp[x][2] % mod) %= mod;
        f[y] = x;
    }
    
    bool Cmp(const int x, const int y) {
        return h[x] > h[y];
    }
    
    int main() {
        Suffix();
        makeSet();
        sort(p + 1, p + n + 1, Cmp);
        for(int i = 1; i <= n; ++ i)
            if(id[i] >= 1)
                dp[i][id[i] - 1] = 1;
        int j = 1;
        for(int i = len; i >= 1; -- i) {
            while(j <= n && h[p[j]] >= i) {
                unionSet(findSet(SA[p[j] - 1]), findSet(SA[p[j]]));
                ++ j;
            }
            ans[i] = tmp;
        }
        for(int i = 1; i <= len; ++ i) printf("%d ", ans[i]);
        putchar('
    ');
        return 0;
    }
    
  • 相关阅读:
    spring cloud的消费服务ribbon(踩着坑往前爬)
    spring cloud注册服务与发现(踩着坑往前爬)
    springboot配置多数据源
    ssh免密登入
    mybatais面试题(复习篇)
    ssh全注解整合
    Vue项目中使用axios配置请求拦截
    Vue后台的路由和权限校验
    CSS3新增属性(4)
    CSS3新增属性(3)
  • 原文地址:https://www.cnblogs.com/AWhiteWall/p/12396259.html
Copyright © 2020-2023  润新知