zoukankan      html  css  js  c++  java
  • 【题解】「2017 山东一轮集训 Day5」字符串

    「2017 山东一轮集训 Day5」字符串

    ( ext{Solution:})

    考虑只有一个串的时候,答案就是所有本质不同子串。

    那么多个串,自然联想到把它们拼在一起建立 SAM.

    但是如果这样,我们匹配到的一定是拼接起来的串上的连续一部分,并不是全部的答案,因为这里除了每一个串内它是可以断开的。

    那只有串内需要连续,我就在串内建立 SAM.

    考虑如何将边转移?我们对于每一个字符显然需要连接到下一个最近的和它相同字符的地方。那我们可以用子序列自动机,或者说维护出每一个点处下一个距离他最近的相同字符的点在哪里,然后进行连边。

    于是这样我们就得到了一个整体的 DAG,它满足:我们在寻找完某一个串后可以跳跃到下一个严格在它后面的串,同时保证可以断开跳跃,且后面的串没办法跳回前面,且每一个串内选择的一定是连续子串。

    那我们就可以在上面 (dp) 了。注意包含空串。

    #include<bits/stdc++.h>
    using namespace std;
    const int N=2e6+10;
    const int mod=1e9+7;
    inline int Add(int x,int y){return (x+y)%mod;}
    inline int Mul(int x,int y){return 1ll*x*y%mod;}
    namespace SAM{
        int len[N],pa[N],f[N],ch[N][26],last=0,tot=0,root;
        void insert(const int &c){
            int p=last;
            int np=++tot;
            last=tot;len[np]=len[p]+1;
            for(;p&&!ch[p][c];p=pa[p])ch[p][c]=np;
            if(!p)pa[np]=root;
            else{
                int q=ch[p][c];
                if(len[q]==len[p]+1)pa[np]=q;
                else{
                    int nq=++tot;
                    len[nq]=len[p]+1;
                    pa[nq]=pa[q];pa[q]=pa[np]=nq;
                    memcpy(ch[nq],ch[q],sizeof ch[q]);
                    for(;p&&ch[p][c]==q;p=pa[p])ch[p][c]=nq;
                }
            }
        }
        void dfs(const int &x){
            if(f[x])return;
            f[x]=1;
            for(int i=0;i<26;++i){
                int v=ch[x][i];
                if(!v)continue;
                dfs(v);
                f[x]=Add(f[x],f[v]);
            }
        }
    }
    using namespace SAM;
    int rt[N],n;
    string s[N];
    int nxtpos[26];
    int main(){
        scanf("%d",&n);
        for(int i=1;i<=n;++i)cin>>s[i];
        for(int i=1;i<=n;++i){
            rt[i]=last=root=++tot;
            for(int j=0;j<(int)s[i].size();++j)insert(s[i][j]-'a');
        }
        rt[n+1]=tot+1;
        for(int i=n;i>=1;--i){
            for(int j=rt[i];j<rt[i+1];++j){
                for(int k=0;k<26;++k)
                    if(!ch[j][k])
                        ch[j][k]=nxtpos[k];
            }
            for(int j=0;j<26;++j)nxtpos[j]=ch[rt[i]][j];
        }
        dfs(1);
        printf("%d
    ",f[1]);
        return 0;
    }
    
  • 相关阅读:
    审核被拒:包含隐藏功能
    iOS好的个人博客和平台网站
    免费的Git和SVN服务器
    组件化
    三方生产利器
    RSA加密解密和签名验证机制以及其区别和联系
    APP和后台接口设计规范
    树和二叉树2——输出广义表形式(带括号)二叉树
    树和二叉树1——链式二叉树基础
    计算机图形学5——Two-Dimensional Viewing and Clipping(二维线段裁剪算法)
  • 原文地址:https://www.cnblogs.com/h-lka/p/15170809.html
Copyright © 2011-2022 走看看