zoukankan      html  css  js  c++  java
  • 【题解】CF235C Cyclical Quest

    CF235C Cyclical Quest

    ( ext{Solution:})

    还是对 SAM 上一些细节的处理和理解……

    首先一个循环相当于是一个串删掉首字母再把它插入到后面形成的。

    观察插入的过程,就是平凡的在 SAM 上面匹配的过程。

    那删除怎么办?我们要知道 SAM 是后缀的自动机!

    所以,当一个点向它的 parent 树上父亲跳的时候, 其父亲必然是其后缀 并且长度严格小于它,这也意味着其父亲相当于它对应的串 删掉几个首字母得到的

    那这一点不就恰好契合了我们 删除首字母 的愿望了吗。于是,我们先对串进行一次匹配,如果当前完全匹配到了,那我们就加上当前节点的 (siz,) 否则就继续下一个字符。

    如果完全匹配好了,由于我们 SAM 上面一个点对应的是许多串 或者说后缀,所以我们直接将匹配长度 (-1) 并判断匹配长度 (-1) 后的串是在当前节点对应还是其父亲对应。

    只考虑其父亲是因为其父亲的最长串长度严格小于它的最短串长度。

    而如果没有完全匹配就可以先不考虑删字符的情况了——毕竟它还没有完全出现过,当前匹配到的首字母也必然是某个后缀的首字母,它可以当后面匹配到的循环的首字母。

    观察匹配的过程:我们需要维护一个当前匹配到的长度。但是注意这里的细节:我们平凡地经过转移边到达某个点的时候,不能直接令匹配长度与这个节点的 (len) 相等。

    这是因为:一个节点的 (len) 定义为这个节点对应的 endpos 集合相同的串中最长的串长度。那我们经过一条转移边到达,显然不一定是到达了最长的那个串,所以这里只能令匹配长度 (+1.)

    但是跳父亲的时候有所不同:跳父亲,意味着我们从之前匹配到的长串向短串跳,那么对应一些后缀相同的串,那显然匹配最长的那个就可以了,这里可以直接令长度与节点的 (len) 相等。

    注意 Ans 要开 long long .

    #include<bits/stdc++.h>
    using namespace std;
    const int N=2e6+10;
    typedef long long ll;
    char s[N];
    namespace SAM{
        int len[N],pa[N],siz[N],ch[N][26];
        int tot,last,slen,vis[N],Ans;
        vector<int>G[N];
        void insert(const int &c){
            int p=last;
            int np=++tot;
            last=tot;
            len[np]=len[p]+1;
            siz[np]=1;
            for(;p&&!ch[p][c];p=pa[p])ch[p][c]=np;
            if(!p)pa[np]=1;
            else{
                int q=ch[p][c];
                if(len[q]==len[p]+1)pa[np]=q;
                else{
                    int nq=++tot;
                    memcpy(ch[nq],ch[q],sizeof ch[q]);
                    len[nq]=len[p]+1;pa[nq]=pa[q];
                    pa[q]=pa[np]=nq;
                    for(;p&&ch[p][c]==q;p=pa[p])ch[p][c]=nq;
                }
            }
        }
        void dfs(int x){
            for(auto i:G[x]){
                dfs(i);
                siz[x]+=siz[i];
            }
        }
        void build(){
            for(int i=2;i<=tot;++i)G[pa[i]].push_back(i);
            for(int i=1;i<=tot;++i)vis[i]=-1;
            dfs(1);
        }
        void solve(int node){
            scanf("%s",s+1);
            slen=strlen(s+1);
            ll Ans=0;
            int now=0;
            int cur=1;
            for(int i=1;i<=slen;++i){
                int v=s[i]-'a';
                while(cur&&!ch[cur][v])cur=pa[cur],now=len[cur];
                if(ch[cur][v])cur=ch[cur][v],now++;
            }
            for(int i=1;i<=slen;++i){
                int v=s[i]-'a';
                if(now==slen){
                    if(vis[cur]!=node)Ans+=siz[cur];
                    vis[cur]=node;
                    if(--now==len[pa[cur]])cur=pa[cur];
                }
                while(cur&&!ch[cur][v])cur=pa[cur],now=len[cur];
                if(ch[cur][v])cur=ch[cur][v],now++;
            }
            printf("%lld
    ",Ans);
        }
    }
    char S[N];
    int T,n;
    signed main(){
        scanf("%s",S+1);
        n=strlen(S+1);SAM::tot=1;SAM::last=1;
        for(int i=1;i<=n;++i)SAM::insert(S[i]-'a');
        SAM::build();
        scanf("%d",&T);
        while(T--){SAM::solve(T);}
        return 0;
    }
    

    (代码长度恰好2021)

  • 相关阅读:
    开源界的 5 大开源许可协议
    如何选择开源许可证?
    Ubuntu下Qt编译报错“cannot find -lGL”的解决方案
    How to Cracked Sublime Text 3 Build 3065 in Ubuntu (Linux)
    一个C语言宏展开问题
    C语言预处理运算符
    Linux线程编程之信号处理
    Linux终端多用户通信实用命令
    守护进程接收终端输入的一种变通性方法(二)
    通过printf设置Linux终端输出的颜色和显示方式
  • 原文地址:https://www.cnblogs.com/h-lka/p/15169021.html
Copyright © 2011-2022 走看看