2019HNCPC C Distinct Substrings 后缀自动机

题意

给定一个长度为n字符串,字符集大小为m(1<=n,m<=1e6),求\(\bigoplus_{c = 1}^{m}\left(h(c) \cdot 3^c \bmod (10^9+7)\right)\)的值。其中h(c)为将c加到字符串末尾产生的新的本质不同的子串数目。

解题思路

比赛的时候没做出来,颁奖的时候听lts和lsx讲了之后发现可以用SAM做,而且板子稍微改改就可以了。

具体就是每次添加一个字符最多新建2个节点,根据SAM的性质,添加c后新建节点对本质不同的子串的数目的贡献就是h(c),用len[i]-len[link[i]]就能算出来。

现场赛的时候我一直没想到添加结点之后要怎么把这个结点再删掉,没有立刻想出来,然后就去做D数位DP了,但是其实很简单,可以把extend函数改一改,对于询问只算答案不修改原来的节点,这样就不需要删除;或者用一个容器记录修改了哪一些结点,询问完了再遍历一遍改回去。这两种方法应该都是\(O(1)\)修改的。

代码实现

和叉姐的标程对拍没有出错,应该是对的吧

#include<bits/stdc++.h>
using namespace std;

typedef long long ll;
const int maxn=1e6+5;
const int mod=1e9+7;

struct Suffix_Automaton{
    struct state{
        int len,link;
        map<int,int>next;
    }st[maxn<<1];
    int last,tot;

    int sz;

    void init(){
        st[1].len=0;st[0].link=0;
        st[1].next.clear();
        last=tot=1;;
    }
    int newnode(){
        ++tot;
        st[tot].len=st[tot].link=0;
        st[tot].next.clear();
        return tot;
    }
    void extend(int c){
        int p=last;
        int cur=newnode();
        st[cur].len=st[last].len+1;
        last=cur;
        while(p && !st[p].next.count(c)){
            st[p].next[c]=cur;
            p=st[p].link;
        }
        if(!p)st[cur].link=1;
        else{
            int q=st[p].next[c];
            if(st[p].len+1==st[q].len)st[cur].link=q;
            else{
                int clone=newnode();
                st[clone].len=st[p].len+1;
                st[clone].next=st[q].next;
                st[clone].link=st[q].link;
                st[q].link=st[cur].link=clone;
                while(st[p].next[c]==q){
                    st[p].next[c]=clone;
                    p=st[p].link;
                }
            }
        }
    }
    ll solve(int c){
        ll res=0;

        int p=last;
        int cur=newnode();
        st[cur].len=st[last].len+1;
        //last=cur;
        while(p && !st[p].next.count(c)){
            //st[p].next[c]=cur;
            p=st[p].link;
        }
        if(!p){
            st[cur].link=1;
            res=st[cur].len-st[st[cur].link].len;
        }
        else{
            int q=st[p].next[c];
            if(st[p].len+1==st[q].len){
                st[cur].link=q;
                res=st[cur].len-st[st[cur].link].len;
            }
            else{
                int clone=newnode();
                st[clone].len=st[p].len+1;
                //st[clone].next=st[q].next;
                st[clone].link=st[q].link;
                //while(p && st[p].next[c]==q){
                    //st[p].next[c]=clone;
                    //p=st[p].link;
                //}
                //st[q].link=clone;
                st[cur].link=clone;

                //结点q的link变为clone,所以需要把原来的删掉再把新的加进去
                res=((res-(st[q].len-st[st[q].link].len))%mod+mod)%mod;
                res=(res+st[q].len-st[clone].len)%mod;

                res=(res+st[cur].len-st[st[cur].link].len)%mod;
                res=(res+st[clone].len-st[st[clone].link].len)%mod;
            }
        }

        tot=sz;
        return (res%mod+mod)%mod;
    }
}S;
int n,m;
int main()
{
    while(~scanf("%d %d",&n,&m)){
        S.init();
        int x;
        for(int i=1;i<=n;i++){
            scanf("%d",&x);
            S.extend(x);
        }
        S.sz=S.tot;
        ll ans=0,three=1,hc;
        for(int i=1;i<=m;i++){
            three=three*3%mod;
            hc=S.solve(i);
            ans^=(1*hc*three%mod);
        }
        printf("%lld\n",ans);
    }
    return 0;
}

原文地址:https://www.cnblogs.com/zengzk/p/11444479.html

时间: 2024-11-05 13:50:09

2019HNCPC C Distinct Substrings 后缀自动机的相关文章

SPOJ694--- DISUBSTR - Distinct Substrings(后缀数组)

Given a string, we need to find the total number of its distinct substrings. Input T- number of test cases. T<=20; Each test case consists of one string, whose length is <= 1000 Output For each test case output one number saying the number of distin

SPOJ 694. Distinct Substrings,705. New Distinct Substrings(后缀数组)

题目大意:给定长度为N的字符串,求出其中不相同子串的个数. 解题思路:每一个字串一定是某个后缀的前缀,那么原问题就可以等价于求所有后缀之间的不相同的前缀的个数.如果所有的后缀按照suffix(sa[1]),suffix(sa[2])--suffix(sa[n])的顺序计算,我们会发现对于每个新加进来的后缀suffix(sa[k]),它将产生n-sa[k]+1个新的前缀.但是其中有leight[k]个是和前面的字符串的前缀是相同的.所以suffix(sa[k])加进来增加的不同的子串的个数为n-s

SPOJ694&amp;&amp;SPOJ705:Distinct Substrings(后缀数组)

Description Given a string, we need to find the total number of its distinct substrings. Input T- number of test cases. T<=20; Each test case consists of one string, whose length is <= 1000 Output For each test case output one number saying the numb

【CF316G3】Good Substrings 后缀自动机

[CF316G3]Good Substrings 题意:给出n个限制(p,l,r),我们称一个字符串满足一个限制当且仅当这个字符串在p中的出现次数在[l,r]之间.现在想问你S的所有本质不同的子串中,有多少个满足所有限制. |S|,|p|<=10^5,n<=10. 题解:比较简单的后缀自动机题,我们先把原串和所有限制串放到一起建一个广义后缀自动机,然后在pre树上统计一下即可得到每个子串在每个限制串中出现了多少次.现在我们想知道原串中有多少满足条件的子串,即我们统计一下所有出现次数符合要求的,

【POJ3415】Common Substrings 后缀自动机

转载请注明出处:http://blog.csdn.net/vmurder/article/details/42710069 其实我就是觉得原创的访问量比未授权盗版多有点不爽233... 题意: 给两个串,问有多少长度大于等于K的公共子串(位置不同也算一对) 题解: 后缀自动机DP 对第一个串建立后缀自动机,然后做一些预处理, 然后拿第二个串在后缀自动机上跑,到每个节点加一次贡献. 但是这样需要每个点往parent树上跑一遍,会TLE,所以可以加个lazy. 然后代码中有两次运用到拓扑序来从子向父

[spoj694&amp;spoj705]New Distinct Substrings(后缀数组)

题意:求字符串中不同子串的个数. 解题关键:每个子串一定是某个后缀的前缀,那么原问题等价于求所有后缀之间的不相同的前缀的个数. 1.总数减去height数组的和即可. 注意这里height中为什么不需要进行组合计数,因为,每一个height的左端点已经确定,所以只需变动右端点,总共$height[i]$种情况. 2.如果所有的后缀按照 suffix(sa[1]), suffix(sa[2]),suffix(sa[3]), …… ,suffix(sa[n])的顺序计算,不难发现,对于每一次新加进来

spoj Distinct Substrings 后缀数组

给定一个字符串,求不相同的子串的个数. 假如给字符串“ABA";排列的子串可能: A B A AB  BA ABA 共3*(3+1)/2=6种; 后缀数组表示时: A ABA BA 对于A和AB height[i]=1; 表明一个长度公共,所以ABA中多出现了A这个子串,所以6-1=5: 对于ABA BA height[i]=0,所以不需要减去. 最后答案为5: #include<iostream> #include<stdio.h> #include<string

SPOJ 694 || 705 Distinct Substrings ( 后缀数组 &amp;&amp; 不同子串的个数 )

题意 : 对于给出的串,输出其不同长度的子串的种类数 分析 : 有一个事实就是每一个子串必定是某一个后缀的前缀,换句话说就是每一个后缀的的每一个前缀都代表着一个子串,那么如何在这么多子串or后缀的前缀中找出不同的并计数呢?思路就是所有的可能子串数 - 重复的子串数.首先我们容易得到一个长度为 len 的串的子串数为 len * ( len + 1) / 2.那如何知道重复的子串数呢?答案就是利用后缀数组去跑一遍 Height ,得到所有的最长公共前缀(LCP),这些最长公共前缀的值都存在了 He

SPOJ8222 Substrings( 后缀自动机 + dp )

题目大意:给一个字符串S,令F(x)表示S的所有长度为x的子串中,出现次数的最大值.F(1)..F(Length(S)) 建出SAM, 然后求出Right, 求Right可以按拓扑序dp..Right就是某个点到结束状态的路径数, parent树上last的那一条链都是结束状态...然后用Right去更新答案.. spoj卡常数..一开始用DFS就炸了, 改用BFS就A了.. (贴一下丽洁姐的题解: 我们构造S的SAM,那么对于一个节点s,它的长度范围是[Min(s),Max(s)],同时他的出