[航海协会]字符串

Posted StaroForgin

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了[航海协会]字符串相关的知识,希望对你有一定的参考价值。

字符串

题目概述


题解

容易发现,上面的这个所谓路径可以被理解成对原字符串的反串建立后缀树,后缀树上的每条边代表的字符串都要计算到答案中一次。
这个是比较好理解的,因为这个反串的后缀树就相当于将题目给出的 T r i e Trie Trie树进行了收缩。

我们考虑将这个反串后缀树转化到正串的后缀自动机上面,显然,这样的一条边在正串的后缀自动机上是一条路径。
由于后缀自动机是一个 D A G DAG DAG,我们的路径也就是 D A G DAG DAG图上的一条路径。
我们观察 D A G DAG DAG上到达同一个点的路径的性质,如果要使得两个黑点之间的路径不经过任何其它的黑点,也就是说中途都是没有分叉,一条路走到底的。
这就意味着整个子串是唯一的,映射到我们正串后缀自动机上,到它的点的路径也是唯一的。所以,我们可以发现到点 x x x的路径在后缀自动机上一定是形如一棵内向树的样子。
同时,这些路径的边也是相同的,短的路径形成的字符串肯定是长的路径形成字符串的后缀。毕竟都在正串后缀自动机上到同一个节点也就意味着在正串的后缀树上的同一个节点,这当然是一样的。
另外,还有一个性质,就是不同终止结点的树边集合是不会有交的,毕竟它们不能跨过其它终止节点。

所以,所有点的树边集合大小总和是不会超过DAG上的边的边数 O ( n ) O(n) O(n)卷爷说的,我证不来
那么我们可以考虑对于每个终止点,选择它出去的最长的路径,做一次后缀自动机,求出每条路径结尾处的方案数,加入答案。
这样,我们就能线性地解决该问题了。

总时间复杂度 O ( n ∣ ∑ ∣ ) O\\left(n|\\sum|\\right) O(n)

源码

#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
typedef pair<int,int> pii;
#define MAXN 1000005
#define MAXM 2000005
#define pb push_back
#define mkpr make_pair
#define fir first
#define sec second
#define lson (rt<<1)
#define rson (rt<<1|1)
const int mo=998244353;
const int jzm=2333;
template<typename _T>
_T Fabs(_T x)return x<0?-x:x;
template<typename _T>
void read(_T &x)
    _T f=1;x=0;char s=getchar();
    while(s<'0'||s>'9')if(s=='-')f=-1;s=getchar();
    while('0'<=s&&s<='9')x=(x<<3)+(x<<1)+(s^48);s=getchar();
    x*=f;

int add(int x,int y,int p)return x+y<p?x+y:x+y-p;
void Add(int &x,int y,int p)x=add(x,y,p);
int qkpow(int a,int s,int p)int t=1;while(s)if(s&1)t=1ll*a*t%p;a=1ll*a*a%p;s>>=1;return t;
int n,m,val[MAXM],sta[MAXM],stak;
char str[MAXN];vector<pii>G[MAXM];
bool ed[MAXM];LL ans,cnt[MAXN];
struct nodeint ch[2],len,fa;;
class SuffixAutomaton
    private:
        node tr[MAXM];int tot,las;LL summ;
    public:
        void init()tr[0].len=-1;las=++tot;
        void extend(int x)
            int p=las,np=las=++tot;tr[np].len=tr[p].len+1;
            for(;p&&!tr[p].ch[x];p=tr[p].fa)tr[p].ch[x]=np;
            summ+=tr[np].len-tr[p].len-1;
            if(!p)tr[np].fa=1;return ;int q=tr[p].ch[x];
            if(tr[q].len==tr[p].len+1)tr[np].fa=q;return ;
            int nq=++tot;tr[nq]=tr[q];tr[nq].len=tr[p].len+1;
            for(;p&&tr[p].ch[x]==q;p=tr[p].fa)tr[p].ch[x]=nq;
            tr[q].fa=tr[np].fa=nq;
        
        LL ask()return summ;
        void sakura()
            for(int i=las;i;i=tr[i].fa)ed[i]=1;m=tot;
            for(int i=1;i<=tot;i++)
                for(int j=0;j<2;j++)if(tr[i].ch[j])
                    G[tr[i].ch[j]].pb(mkpr(i,j));
                if(tr[i].ch[0]&&tr[i].ch[1])ed[i]=1;
                val[i]=tr[i].len-tr[tr[i].fa].len;
            
        
        void clear()
            for(int i=1;i<=tot;i++)
                tr[i].ch[0]=tr[i].ch[1]=tr[i].fa=tr[i].len=0;
            tot=las=summ=0;
        
T,P;
void dfs(int x,int d)
    int siz=G[x].size();if(d&&ed[x])cnt[d]+=val[x];return ;
    for(int i=0;i<siz;i++)
        int v=G[x][i].fir,w=G[x][i].sec;
        if(d==stak)sta[stak=d+1]=w;dfs(v,d+1);
    

int main()
    freopen("string.in","r",stdin);
    freopen("string.out","w",stdout);
    scanf("%s",str+1);n=(int)strlen(str+1);T.init();
    for(int i=1;i<=n;i++)T.extend(str[i]-'0');T.sakura();
    for(int i=2;i<=m;i++)if(ed[i])
        dfs(i,0);P.init();
        for(int j=1;j<=stak;j++)
            P.extend(sta[j]),ans+=cnt[j]*P.ask(),cnt[j]=0;
        P.clear();stak=0;
    
    printf("%lld\\n",ans);
    return 0;

谢谢!!!

以上是关于[航海协会]字符串的主要内容,如果未能解决你的问题,请参考以下文章

[航海协会]万灵药

[航海协会]SSSP

[航海协会]稀疏阶乘问题

[航海协会]身体

[航海协会]身体

[航海协会]求和