zoukankan      html  css  js  c++  java
  • BZOJ3238 [Ahoi2013]差异 【SAM or SA】

    BZOJ3238 [Ahoi2013]差异

    给定一个串,问其任意两个后缀的最长公共前缀长度的和

    1.又是后缀,又是(lcp),很显然直接拿(SA)(height)数组搞就好了,配合一下单调栈

    //#pragma GCC optimize("O3")
    //#pragma comment(linker, "/STACK:1024000000,1024000000")
    #include<bits/stdc++.h>
    using namespace std;
    function<void(void)> ____ = [](){ios_base::sync_with_stdio(false); cin.tie(0); cout.tie(0);};
    using LL = int_fast64_t;
    const int MAXN = 5e5+7;
    struct SA{
        int sa[MAXN],rk[MAXN],c[MAXN],sec[MAXN],height[MAXN],n;
        char s[MAXN];
        void getsa(int m){
            n = strlen(s+1);
            for(int i = 0; i <= m; i++) c[i] = 0;
            for(int i = 1; i <= n; i++) c[rk[i]=s[i]]++;
            for(int i = 1; i <= m; i++) c[i] += c[i-1];
            for(int i = n; i >= 1; i--) sa[c[rk[i]]--] = i;
            for(int k = 1; k <= n; k <<= 1){
                int p = 0;
                for(int i = n - k + 1; i <= n; i++) sec[++p] = i;
                for(int i = 1; i <= n; i++) if(sa[i]>k) sec[++p] = sa[i] - k;
                for(int i = 0; i <= m; i++) c[i] =  0;
                for(int i = 1; i <= n; i++) c[rk[sec[i]]]++;
                for(int i = 1; i <= m; i++) c[i] += c[i-1];
                for(int i = n; i >= 1; i--) sa[c[rk[sec[i]]]--] = sec[i];
                p = 0;
                swap(rk,sec);
                rk[sa[1]] = ++p;
                for(int i = 2; i <= n; i++) rk[sa[i]] = sec[sa[i]]==sec[sa[i-1]] and sec[sa[i]+k]==sec[sa[i-1]+k] ? p : ++p;
                if(p==n) break;
                m = p;
            }
        }
        void getheight(){
            int k = 0;
            for(int i = 1; i <= n; i++){
                if(k) k--;
                int j = sa[rk[i]-1];
                while(s[i+k]==s[j+k]) k++;
                height[rk[i]] = k;
            }
        }
        LL solve(){
            getsa(128);
            getheight();
            LL ret = (n+1ll) * n * (n-1ll) / 2;
            stack<pair<int,int> > stk;
            LL tot = 0;
            for(int i = 1; i <= n; i++){
                int num = 1;
                while(!stk.empty() and height[i]<=height[stk.top().first]){
                    num += stk.top().second;
                    tot -= 1ll * stk.top().second * (height[stk.top().first] - height[i]);
                    stk.pop();
                }
                stk.push(make_pair(i,num));
                tot += height[i];
                ret -= tot * 2;
            }
            return ret;
        }
    }sa;
    char s[MAXN];
    int main(){
        scanf("%s",sa.s+1);
        printf("%lld
    ",sa.solve());
        return 0;
    }
    

    2.考虑用(SAM)来做,把串反转一下,现在要计算的是任意两个前缀的最长公共后缀的和
    (SAM)有个性质,那就是两个点的(LCA)的状态表示的最长串,是这两个点表示的子串的最长公共后缀
    所以我们可以枚举所有的(LCA),然后计算每一个(LCA)的贡献,先计算其任意两棵子树对应的贡献,再计算子树和当前节点产生的贡献

    //#pragma GCC optimize("O3")
    //#pragma comment(linker, "/STACK:1024000000,1024000000")
    #include<bits/stdc++.h>
    using namespace std;
    using LL = int_fast64_t;
    const int MAXN = 1e6+7;
    struct SAM{
        int len[MAXN],link[MAXN],ch[MAXN][26],cnt[MAXN],tot,last;
        vector<int> G[MAXN];
        SAM(){ link[0] = -1; }
        void extend(int c){
            int np = ++tot, p = last;
            len[np] = len[last] + 1; cnt[np] = 1;
            while(p!=-1 and !ch[p][c]){
                ch[p][c] = np;
                p = link[p];
            }
            if(p==-1) link[np] = 0;
            else{
                int q = ch[p][c];
                if(len[p]+1==len[q]) link[np] = q;
                else{
                    int clone = ++tot;
                    len[clone] = len[p] + 1;
                    link[clone] = link[q];
                    memcpy(ch[clone],ch[q],sizeof(ch[q]));
                    link[np] = link[q] = clone;
                    while(p!=-1 and ch[p][c]==q){
                        ch[p][c] = clone;
                        p = link[p];
                    }
                }
            }
            last = np;
        }
        void dfs(int u, LL &ret){
            int all = 0;
            for(int i = 0; i < (int)G[u].size(); i++){
                int v = G[u][i];
                dfs(v,ret);
                all += cnt[v];
            }
            for(int i = 0; i < (int)G[u].size(); i++){
                int v = G[u][i];
                ret -= 1ll * (all - cnt[v]) * cnt[v] * len[u];
            }
            ret -= 2ll * all * cnt[u] * len[u];
            cnt[u] += all;
        }
        void solve(char *s){
            int n = strlen(s);
            for(int i = 0; i < n; i++) extend(s[i]-'a');
            for(int i = 1; i <= tot; i++) G[link[i]].push_back(i);
            LL ret = n * (n-1ll) * (n+1ll) / 2;
            dfs(0,ret);
            printf("%lld
    ",ret);
        }
    }sam;
    char s[MAXN];
    int main(){
        scanf("%s",s);
        sam.solve(s);
        return 0;
    }
    
  • 相关阅读:
    some Rails leanrning:Rails Ajax,Validates,Cycle
    快速排序与挑白菜
    九月开学
    搜索引擎代码资源
    start RubyOnRails
    垂直搜索引擎之我见
    demo下载:ASP.NET GDI+生成动态的Gif动画
    NHibernate和ADO.Net的性能对比相差有多大?
    手工调试自定义控件各主要方法执行顺序(分运行时和设计时)
    ASP.Net中的缓存方案(不仅仅是Cache和Session)(我在CSDN上和别人的争论)
  • 原文地址:https://www.cnblogs.com/kikokiko/p/12713059.html
Copyright © 2011-2022 走看看