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;
}

posted @ 2017-01-29 14:52  Miao_miao  阅读(125)  评论(0编辑  收藏  举报