zoukankan      html  css  js  c++  java
  • BZOJ4566 [Haoi2016]找相同字符【SAM】

    BZOJ4566 [Haoi2016]找相同字符

    给定两个字符串(s和t),要求找出两个字符串中所有可以相互匹配的子串对的数量
    首先考虑可以怎么做,我们可以枚举(t)串的前缀(t'),然后找(t')的后缀能和(s)串匹配上的数量
    这部分做法和和求(LCS)差不多
    我们首先根据(s)串建(SAM),然后计算出每个状态的(endpos)集合大小,我们现在想知道以当前状态(u)最长串为后缀最多可以匹配多少子串,那么当前状态可以匹配的数量就是((len[u]-len[link[u]])cdot cnt[u]),其中(len[u])为状态(u)的最长串的长度(cnt[u])(endpos)集合的大小,即匹配位置数量乘上可以匹配的长度,同时如果当前状态能够匹配,那么其后缀链接所连的状态也都能匹配,因为后缀链接连的是当前状态的后缀,既然要匹配的串的后缀能匹配的状态(u),那么必然能匹配到(link[u],link[link[u]]cdots),所以我们要把(parent)中父节点的贡献下传,记当前状态(u)的最长串为后缀最多可以匹配的子串数量为(f[u])
    现在预处理完(s)串之后,我们在(SAM)上跑(t)串,对于每一个前缀,就相当于在上一个前缀的后面加上一个字符,那么就是在之前串所到的状态往后跑,如果上一个状态没有新的字符的连边,那么就跑他的后缀链接,直到到初始点或者找到有连边的节点,然后统计当前状态的答案,答案就是(f[link[u]]+cnt[u]cdot (matchlen-len[link[u]])),其中(matchlen)(t)串的这个前缀(t')的后缀和(s)串能匹配的最长长度,由于当前状态里的所有长度不一定都可以匹配,所以不能直接算加上(f[u]),具体可以看代码

    //#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);};
    typedef long long int LL;
    const int MAXN = 4e5+7;
    struct SAM{
        int len[MAXN],link[MAXN],ch[MAXN][26],tot,last,cnt[MAXN],c[MAXN],sa[MAXN];
        LL f[MAXN];
        SAM(){ link[0] = -1; }
        void extend(int c){
            int np = ++tot, p = last;
            len[np] = len[p] + 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 Radix_sort(){
            for(int i = 0; i <= tot; i++) c[i] = 0;
            for(int i = 0; i <= tot; i++) c[len[i]]++;
            for(int i = 1; i <= tot; i++) c[i] += c[i-1];
            for(int i = tot; i >= 0; i--) sa[c[len[i]]--] = i;
        }
        LL solve(char *s){
            Radix_sort();
            for(int i = tot + 1; i >= 2; i--) cnt[link[sa[i]]] += cnt[sa[i]];
            for(int i = 2; i <= tot + 1; i++){
                int u = sa[i];
                f[u] = f[link[u]] + 1ll * cnt[u] * (len[u] - len[link[u]]); 
            }
            int u = 0, ls = 0;
            LL ret = 0;
            for(int i = 0, l = strlen(s); i < l; i++){
                int c = s[i] - 'a';
                while(u and !ch[u][c]) u = link[u], ls = len[u];
                if(ch[u][c]) u = ch[u][c], ls++;
                if(!u) continue;
                ret = ret + f[link[u]] + (ls-len[link[u]]) * cnt[u];
            }
            return ret;
        }
    }sam;
    char s[MAXN];
    int main(){
        scanf("%s",s);
        for(int i = 0, l = strlen(s); i < l; i++) sam.extend(s[i]-'a');
        scanf("%s",s);
        printf("%lld
    ",sam.solve(s));
        return 0;
    }
    
  • 相关阅读:
    一个关于Delphi XML处理单元的BUG
    弹出一个非阻塞对话框
    更新Delphi中SVN客户端版本的方法
    程序只允许运行一个+重复运行程序提前
    Reverse Words in a String
    How to define Servlet filter order of execution using annotations
    Understanding and Using Servlet Filters
    Observer Pattern
    javascript 比较
    SOAP vs REST
  • 原文地址:https://www.cnblogs.com/kikokiko/p/12706855.html
Copyright © 2011-2022 走看看