zoukankan      html  css  js  c++  java
  • NOI2016 优秀的拆分 后缀数组

    题目链接洛谷点我:-) UOJ点我:-)
    题目描述
    如果一个字符串可以被拆分为 AABB 的形式,其中 A和 B是任意非空字符串,则我们称该字符串的这种拆分是优秀的。
    例如,对于字符串 aabaabaa,如果令 A=aab,B=a,我们就找到了这个字符串拆分成 AABB的一种方式。
    一个字符串可能没有优秀的拆分,也可能存在不止一种优秀的拆分。比如我们令 A=a,B=baa,也可以用 AABB表示出上述字符串;但是,字符串 abaabaa 就没有优秀的拆分。
    现在给出一个长度为 n(n <= 30000)的字符串 S,我们需要求出,在它所有子串的所有拆分方式中,优秀拆分的总个数。这里的子串是指字符串中连续的一段。
    以下事项需要注意:
    1.出现在不同位置的相同子串,我们认为是不同的子串,它们的优秀拆分均会被记入答案。
    2.在一个拆分中,允许出现 A=B。例如 cccc 存在拆分 A=B=c。
    3.字符串本身也是它的一个子串。

    输入格式
    每个输入文件包含多组数据。输入文件的第一行只有一个整数 T,表示数据的组数。保证 1≤T≤10。
    接下来 T行,每行包含一个仅由英文小写字母构成的字符串 S,意义如题所述。

    输出格式
    输出 T行,每行包含一个整数,表示字符串 S 所有子串的所有拆分中,总共有多少个是优秀的拆分。

    思路
    求两个数组:st[i]与en[i],分别表示以i这个字符开头与以i这个字符结尾的‘AA’形式的串有多少个,那么答案就是n1i=1st[i+1]en[i],难点在于如何求出这两个数组:

    我们枚举一个长度L表示当前找的‘AA’型串的长度的一半,枚举i=kL,j=i+L,
    x=lcp(suf(i),suf(j)), 记y=lcs(pre(i1),pre(j1))
    如果x+y>=L,记t=x+yL+1,表示我们找到了t个长度为2L的’AA‘串
    (自己可以举个例子看看,如’abcabcab‘)

    为了方便理解,假设x+y=L,那么我们恰好找到一个(iy,j+x1)的’AA‘串。
    但是每次有一个连续区间,我们不能一个一个加上,因为时效会出问题。所以用到差分的思想(当然有闲心写线段树也是可以的),在区间开始的地方加一,在区间结束的后一个位置减一,那么最后做一遍前缀和即可。

    注意:每次要清c1, c2(即x, y)数组,因为后面的“y[sa[i]+k]==y[sa[i-1]+k]”可能会越限

    感想
    这题最开始用kmp写了个95分暴力,觉得可以了,就没做了。然后某一天发现小伙伴都写了满分啊,吓得我赶紧写
    真心想不出正解,然而网上的题解又很敷衍,幸亏有DY大神的帮助,不然我就做不出来了qwq。有点难理解,其实也还好啦。但是。。。正解是怎么想到的qwq
    毕竟代码不难写,所以妙妙以后要多练思维(-_-|||)

    想想,觉得本题也可以用hash解决,找个时间写写把代码再贴上来吧。。。

    代码

    95分大暴力

    //miaomiao 2016.8.2
    #include<cstdio>
    #include<cstdlib>
    #include<cstring>
    #include<cmath>
    #include<iostream>
    #include<algorithm>
    #include<vector>
    
    using namespace std;
    
    #define MAXS (2000+5)
    
    int f[MAXS][MAXS];
    vector<int> xh[MAXS];
    
    void init(){
        for(int i = 0; i < MAXS; i++) xh[i].clear();
    }
    
    void get_fail(int now, char *s){
        f[now][0] = f[now][1] = 0;
        int slen = strlen(s);
    
        for(int i = 1; i < slen; i++){
            int j = f[now][i];
            while(j && s[i] != s[j]) j = f[now][j];
            f[now][i+1] = s[i]==s[j]? j+1: 0;
        }
    }
    
    char s[MAXS];
    
    int main(){
        int T;
        scanf("%d", &T);
    
        while(T--){
            init();
            scanf("%s", s);
    
            int slen = strlen(s);
            for(int i = 0; i < slen; i++){
                get_fail(i, s+i);
    
                for(int j = 1; j <= (slen-i); j++){
                    if(j % (j-f[i][j])) continue;
    
                    int p = j / (j-f[i][j]);
                    if(p % 2) continue;
                    xh[i].push_back((j+1)/2);
                }
            }
    
            int ans = 0;
            for(int i = 0; i < slen; i++){
                for(int j = 0; j < xh[i].size(); j++){
                    int len = xh[i][j];
                    int startB = i+len*2;
    
                    ans += xh[startB].size();
                }
            }
    
            printf("%d\n", ans);
        }
    
        return 0;
    }
    

    看了题解的正解

    //miaomiao 2017.1.28
    #include<cstdio>
    #include<cstdlib>
    #include<cstring>
    #include<iostream>
    #include<algorithm>
    
    using namespace std;
    
    #define LL long long
    #define Set(a, v) memset(a, v, sizeof(a))
    #define For(i, a, b) for(int i = (a); i <= (int)(b); i++)
    #define Forr(i, a, b) for(int i = (a); i >= (int)(b); i--)
    
    #define LOG (15+5)
    #define MAXN (30000+5)
    
    int n, c[MAXN], c1[MAXN], c2[MAXN], Log[MAXN];
    
    struct SuffixArray{
        char s[MAXN];
        int sa[MAXN], h[MAXN][LOG], rank[MAXN];
    
        void init(){
            Set(c1, 0); Set(c2, 0);
        }
    
        void buildsa(int m='z'){
            int *x = c1, *y = c2;
            For(i, 1, m) c[i] = 0;
            For(i, 1, n) c[x[i]=s[i]]++; 
            For(i, 1, m) c[i] += c[i-1];
            Forr(i, n, 1) sa[c[x[i]]--] = i;
    
            int p;
            for(int k = 1; k <= n; k <<= 1){
                p = 0;
                For(i, n-k+1, n) y[++p] = i;
                For(i, 1, n) if(sa[i] > k) y[++p] = sa[i]-k;    
    
                For(i, 1, m) c[i] = 0;
                For(i, 1, n) c[x[y[i]]]++;
                For(i, 1, m) c[i] += c[i-1];
                Forr(i, n, 1) sa[c[x[y[i]]]--] = y[i];
    
                swap(x, y);
                p = x[sa[1]] = 1;
                For(i, 2, n) x[sa[i]] = (y[sa[i]]==y[sa[i-1]] && y[sa[i]+k]==y[sa[i-1]+k])? p: ++p;
                if(p >= n) break;
                m = p;
            }
        }
    
        void getheight(){
            For(i, 1, n) rank[sa[i]] = i;
            int k = 0, j;
    
            For(i, 1, n){
                if(k) k--;
                j = sa[rank[i]+1];
                if(rank[i] == n) continue;
    
                while(s[i+k] == s[j+k]) k++;
                h[rank[i]][0] = k;
            }
    
            For(j, 1, 15) For(i, 1, n){
                if(i+(1<<(j-1)) > n) break;
                h[i][j] = min(h[i][j-1], h[i+(1<<(j-1))][j-1]);
            }
        }
    
        int LCP(int x, int y){
            x = rank[x]; y = rank[y];
            if(x > y) swap(x, y);
    
            int k = Log[y-x];    
            return min(h[x][k], h[y-(1<<k)][k]);
        }
    }A, B;
    
    int st[MAXN], en[MAXN];
    
    int main(){
        For(i, 2, MAXN-1) Log[i] = Log[i>>1]+1;
    
        int T;
        scanf("%d", &T);
        while(T--){
            A.init(); B.init();
            Set(st, 0); Set(en, 0);
    
            scanf("%s", A.s+1);
            n = strlen(A.s+1);
            For(i, 1, n) B.s[i] = A.s[n-i+1];
    
            A.buildsa(); A.getheight();
            B.buildsa(); B.getheight();
    
            int j, x, y, t;
            For(L, 1, n/2){
                for(int i=L, j=i+L; j <= n; i+=L,j+=L){
                    x = min(A.LCP(i,j), L), y = min(B.LCP(n-(i-1)+1, n-(j-1)+1), L-1);
                    t = x+y-L+1;
                    if(x+y >= L){
                        st[i-y]++; st[i-y+t]--;
                        en[j+x-t]++; en[j+x]--;
                    }
                }
            }
            For(i, 1, n) st[i]+=st[i-1], en[i]+=en[i-1];
    
            LL ans = 0;
            For(i, 1, n) ans += 1LL*en[i]*st[i+1];
            printf("%lld\n", ans);
        }
    
        return 0;
    }
    
    
    Miaomiao❤ ++RP
  • 相关阅读:
    PHP——文件操作
    PHP——注册页面,审核页面,登录页面:加Session和Cookie
    ajax——优化0126(增删改查:添加查看详情,返回结果类型为JSON型,在窗口显示)
    ajax——三级联动下拉列表框的优化(简化页面,用jquery插件代替原来页面代码,返回处理数据类型为"TEXT")
    ajax——实现三级联动下拉列表
    通过view实现字段的只读、隐藏操作【转】
    OpenERP how to set the tree view limit
    OpenERP 疑问之一
    Django 安装
    OpenERP 中国财务模块 调整
  • 原文地址:https://www.cnblogs.com/miaomiao1220/p/6642347.html
Copyright © 2011-2022 走看看