bzoj3756pty的字符串(后缀自动机+计数)

题目描述

题解

我们可以先对trie树建出广义SAM,然后维护一下right集合大小(注意right集合在广义SAM上的维护方式)。

然后把匹配穿往广义SAM上匹配,假设现在匹配到了x节点,那么x的所有祖先后可以被匹配上,那么一个节点的贡献即为r[x]*(l[x]-l[fa[x]])

维护这玩意的和就好了,最下面的节点特判一下。

代码

#include<iostream>
#include<cstdio>
#include<cstring>
#define N 1600002
#define M 8000002
using namespace std;
typedef long long ll;
char c[1],s[M];
int cnt,n,father,pa[N],len,tong[N],rnk[N];
ll sum[N],ans;
int l[N],ch[N][26],fa[N],r[N];
inline int rd(){
    int x=0;char c=getchar();bool f=0;
    while(!isdigit(c)){if(c==‘-‘)f=1;c=getchar();}
    while(isdigit(c)){x=(x<<1)+(x<<3)+(c^48);c=getchar();}
    return f?-x:x;
}
inline int ins(int last,int x){
    int p=last;
    if(ch[p][x]){
        int q=ch[p][x];
        if(l[p]+1==l[q]){r[q]++;return q;}
        else{
            int nq=++cnt;l[nq]=l[p]+1;r[nq]=1;//care!!!!!!!!!!!!!!!
            memcpy(ch[nq],ch[q],sizeof(ch[q]));
            fa[nq]=fa[q];fa[q]=nq;
            for(;ch[p][x]==q;p=fa[p])ch[p][x]=nq;
            return nq;
        }
    }
    else{
        int np=++cnt;l[np]=l[p]+1;r[np]=1;
        for(;p&&!ch[p][x];p=fa[p])ch[p][x]=np;
        if(!p)fa[np]=1;
        else{
            int q=ch[p][x];
            if(l[p]+1==l[q])fa[np]=q;
            else{
                int nq=++cnt;l[nq]=l[p]+1;
                memcpy(ch[nq],ch[q],sizeof(ch[q]));
                fa[nq]=fa[q];fa[q]=fa[np]=nq;
                for(;ch[p][x]==q;p=fa[p])ch[p][x]=nq;
            }
        }
        return np;
    }
}
int main(){
    n=rd();cnt=1;pa[1]=1;
    for(int i=2;i<=n;++i){
        father=rd();scanf("%s",c);
        pa[i]=ins(pa[father],c[0]-‘a‘);
    }
    scanf("%s",s+1);len=strlen(s+1);
    for(int i=1;i<=cnt;++i)tong[l[i]]++;
    for(int i=1;i<=n;++i)tong[i]+=tong[i-1];
    for(int i=1;i<=cnt;++i)rnk[tong[l[i]]--]=i;
    for(int i=cnt;i>=1;--i){int x=rnk[i];r[fa[x]]+=r[x];}
    r[1]=0;
    for(int i=1;i<=cnt;++i){int x=rnk[i];sum[x]=sum[fa[x]]+1ll*(l[x]-l[fa[x]])*r[x];}
    int now=1,le=0;
    for(int i=1;i<=len;++i){
        if(ch[now][s[i]-‘a‘])le++,now=ch[now][s[i]-‘a‘];
        else{
            for(;now&&!ch[now][s[i]-‘a‘];now=fa[now]);
            if(now)le=l[now]+1,now=ch[now][s[i]-‘a‘];
            else le=0,now=1;
        }
        if(now!=1)ans+=sum[fa[now]]+1ll*(le-l[fa[now]])*r[now];
    }
    cout<<ans;
    return 0;
}
#include<iostream>
#include<cstdio>
#include<cstring>
#define N 1600002
#define M 8000002
using namespace std;
typedef long long ll;
char c[1],s[M];
int cnt,n,father,pa[N],len,tong[N],rnk[N];
ll sum[N],ans;
int l[N],ch[N][26],fa[N],r[N];
inline int rd(){
    int x=0;char c=getchar();bool f=0;
    while(!isdigit(c)){if(c==‘-‘)f=1;c=getchar();}
    while(isdigit(c)){x=(x<<1)+(x<<3)+(c^48);c=getchar();}
    return f?-x:x;
}
inline int ins(int last,int x){
    int p=last;
    if(ch[p][x]){
        int q=ch[p][x];
        if(l[p]+1==l[q]){r[q]++;return q;}
        else{
            int nq=++cnt;l[nq]=l[p]+1;r[nq]=1;//care!!!!!!!!!!!!!!!
            memcpy(ch[nq],ch[q],sizeof(ch[q]));
            fa[nq]=fa[q];fa[q]=nq;
            for(;ch[p][x]==q;p=fa[p])ch[p][x]=nq;
            return nq;
        }
    }
    else{
        int np=++cnt;l[np]=l[p]+1;r[np]=1;
        for(;p&&!ch[p][x];p=fa[p])ch[p][x]=np;
        if(!p)fa[np]=1;
        else{
            int q=ch[p][x];
            if(l[p]+1==l[q])fa[np]=q;
            else{
                int nq=++cnt;l[nq]=l[p]+1;
                memcpy(ch[nq],ch[q],sizeof(ch[q]));
                fa[nq]=fa[q];fa[q]=fa[np]=nq;
                for(;ch[p][x]==q;p=fa[p])ch[p][x]=nq;
            }
        }
        return np;
    }
}
int main(){
    n=rd();cnt=1;pa[1]=1;
    for(int i=2;i<=n;++i){
        father=rd();scanf("%s",c);
        pa[i]=ins(pa[father],c[0]-‘a‘);
    }
    scanf("%s",s+1);len=strlen(s+1);
    for(int i=1;i<=cnt;++i)tong[l[i]]++;
    for(int i=1;i<=n;++i)tong[i]+=tong[i-1];
    for(int i=1;i<=cnt;++i)rnk[tong[l[i]]--]=i;
    for(int i=cnt;i>=1;--i){int x=rnk[i];r[fa[x]]+=r[x];}
    r[1]=0;
    for(int i=1;i<=cnt;++i){int x=rnk[i];sum[x]=sum[fa[x]]+1ll*(l[x]-l[fa[x]])*r[x];}
    int now=1,le=0;
    for(int i=1;i<=len;++i){
        if(ch[now][s[i]-‘a‘])le++,now=ch[now][s[i]-‘a‘];
        else{
            for(;now&&!ch[now][s[i]-‘a‘];now=fa[now]);
            if(now)le=l[now]+1,now=ch[now][s[i]-‘a‘];
            else le=0,now=1;
        }
        if(now!=1)ans+=sum[fa[now]]+1ll*(le-l[fa[now]])*r[now];
    }
    cout<<ans;
    return 0;
}

原文地址:https://www.cnblogs.com/ZH-comld/p/10184192.html

时间: 2024-12-19 07:30:12

bzoj3756pty的字符串(后缀自动机+计数)的相关文章

字符串后缀自动机:Directed Acyclic Word Graph

trie -- suffix tree -- suffix automa 有这么一些应用场景: 即时响应用户输入的AJAX搜索框时, 显示候选列表. 搜索引擎的关键字个数统计. 后缀树(Suffix Tree): 从根到叶子表示一个后缀. 仅仅从这一个简单的描述,我们可以概念上解决下面的几个问题: P:查找字符串o是否在字符串S中 A:若o在S中,则o必然是S的某个后缀的前缀. 用S构造后缀树,按在trie中搜索字串的方法搜索o即可. P: 指定字符串T在字符串S中的重复次数. A: 如果T在S

[数据结构]后缀自动机

前言 对于字符串 \(s\) ,\(|s|\) 表示s的长度 对于字符集 \(A\) , \(|A|\) 表示 \(A\) 的大小 本文字符串下标一律从0开始. 本文字数较多,如有错别字或者概念性错误,请联系博主或在下方回复. SAM 后缀自动机 (suffix automaton, SAM) 是一种解决多种字符串问题的数据结构. SAM基于一个字符串构建的,是给定字符串的所有子串的压缩形式. 标准定义为: 字符串 \(s\) 的SAM是一个接受 \(s\) 的所有后缀的最小 \(\texttt

poj 2774 最长公共子串--字符串hash或者后缀数组或者后缀自动机

http://poj.org/problem?id=2774 想用后缀数组的看这里:http://blog.csdn.net/u011026968/article/details/22801015 本文主要讲下怎么hash去找 开始的时候写的是O(n^2 logn)算法 果断超时...虽然也用了二分的,, 代码如下: //hash+二分 #include <cstdio> #include <cstring> #include <algorithm> #include

【字符串数据结构后缀系列Part3】后缀自动机的性质和应用

学会了构建SAM之后,我们要开始学如何使用SAM来处理各种问题了. 我们先来整体看一下SAM的性质(引自2015国家集训队论文集张天扬<后缀自动机及其应用>): 1.每个状态s代表的串的长度是区间(lenfas,lens]. 2.对于每个状态s,它代表的所有串在原串中出现次数和每次出现的右端点相同. 3.在后缀自动机的Parent树中,每个状态的right集合都是其父状态right集合的子集. 4.后缀自动机的Parent树是原串的反向前缀树 . 5.两个串的最长公共后缀,位于这两个串对应状态

51nod1469 淋漓字符串(后缀自动机)

题目大意: 首先,我们来定义一下淋漓尽致子串. 1.令原串为S. 2.设子串的长度为len,在原串S中出现的次数为k,令其出现的位置为p1, p2, ....pk(即这个子串在原串中[pi,pi + len - 1]中出现). 3.若k=1,则该子串不是淋漓尽致子串. 4.若存在pi,pj(i != j),使得S[pi - 1] = S[pj - 1],则该子串不是淋漓尽致子串. 5.若存在pi,pj(i != j),使得S[pi + len] = S[pj + len],则该字串不是淋漓尽致字

POJ 1509 Glass Beads 后缀自动机 模板 字符串的最小表示

http://poj.org/problem?id=1509 后缀自动机其实就是一个压缩储存空间时间(对节点重复利用)的储存所有一个字符串所有子串的trie树,如果想不起来长什么样子可以百度一下找个图回忆,从0开始到任意一个点的串都是字符串的子串. 有一些很好用的性质. 字符串的最小表示就是把一个字符串首尾相连再从任意一个地方断开产生的字典序最小的字符串,这个题是求最小表示的开头字母在原字符串中的下标(从1开始). 具体看实现吧,没什么可以解释的地方. 1 #include<iostream>

【BZOJ1396】识别子串&amp;【BZOJ2865】字符串识别(后缀自动机)

[BZOJ1396]识别子串&[BZOJ2865]字符串识别(后缀自动机) 题面 自从有了DBZOJ 终于有地方交权限题了 题解 很明显,只出现了一次的串 在\(SAM\)的\(right/endpos\)集合大小一定为\(1\) 换句话说,在\(parent\)树上是叶子节点 找到所有这样的节点, 假设它的\(len=r\),它父亲的\(len=p\),它的结束位置为显然就是\(r\) 令\(l=r-p\) 以\(r\)结尾, 并且只出现了一次的串的左端点 为\(1..l\),那么,他们的答案

BZOJ 3926: [Zjoi2015]诸神眷顾的幻想乡 广义后缀自动机 后缀自动机 字符串

https://www.lydsy.com/JudgeOnline/problem.php?id=3926 广义后缀自动机是一种可以处理好多字符串的一种数据结构(不像后缀自动机只有处理一到两种的时候比较方便). 后缀自动机可以说是一种存子串的缩小点数的trie树,广义后缀自动机就是更改了一下塞点的方式让它可以塞多个子串. 1 #include<iostream> 2 #include<cstdio> 3 #include<algorithm> 4 #include<

HDU4622 (查询一段字符串的不同子串个数,后缀自动机)

http://acm.hdu.edu.cn/showproblem.php?pid=4622 题意:给出一个字符串和q次询问,每次询问[l,r]区间内不同子串的个数 分析: N<=2000. 我是用后缀自动机预处理出所有区间的不同子串个数. 建立n次后缀自动机. 为什么要建立N次呢? 因为鸭 , 后缀自动机之所以有继承性是因为定义的起点是相同的 , 而起点不同是没有继承性的  , 所以要枚举n次不同的节点建立后缀自动机. 用一个变量t , 不断的累加建立到当前点有多少个不同的字符串 , 就是前缀