bzoj 4566 找相同字符 —— 广义后缀自动机

题目:https://www.lydsy.com/JudgeOnline/problem.php?id=4566

建出两个串的广义后缀自动机;

统计每个点在两个串中出现次数的子树和,其实就是在两个串中的 right 集合大小;

然后统计答案乘起来即可。

代码如下:

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
typedef long long ll;
int const xn=8e5+5;
int n,m,cnt=1,fa[xn],go[xn][30],l[xn],tax[xn],q[xn],d1[xn],d2[xn];
char dc[xn];
int work(int p,int w)
{
  int nq=++cnt,q=go[p][w]; l[nq]=l[p]+1;
  fa[nq]=fa[q]; fa[q]=nq;
  memcpy(go[nq],go[q],sizeof go[q]);
  for(;p&&go[p][w]==q;p=fa[p])go[p][w]=nq;
  return nq;
}
int ext(int p,int w)
{
  if(go[p][w])
    {
      int q=go[p][w];
      if(l[q]==l[p]+1)return q; return work(p,w);
    }
  int np=++cnt; l[np]=l[p]+1;
  for(;p&&!go[p][w];p=fa[p])go[p][w]=np;
  if(!p)fa[np]=1;
  else
    {
      int q=go[p][w];
      if(l[q]==l[p]+1)fa[np]=q;
      else fa[np]=work(p,w);
    }
  return np;
}
void rsort()
{
  for(int i=1;i<=cnt;i++)tax[l[i]]++;
  for(int i=1;i<=cnt;i++)tax[i]+=tax[i-1];
  for(int i=cnt;i;i--)q[tax[l[i]]--]=i;
}
int main()
{
  scanf("%s",dc); n=strlen(dc);
  for(int lst=1,i=0;i<n;i++)lst=ext(lst,dc[i]-‘a‘+1),d1[lst]++;
  scanf("%s",dc); m=strlen(dc);
  for(int lst=1,i=0;i<n;i++)lst=ext(lst,dc[i]-‘a‘+1),d2[lst]++;
  rsort();
  for(int i=cnt,x;i;i--)d1[fa[x=q[i]]]+=d1[x],d2[fa[x]]+=d2[x];
  ll ans=0;
  for(int i=1;i<=cnt;i++)ans+=(ll)(l[i]-l[fa[i]])*d1[i]*d2[i];
  printf("%lld\n",ans);
  return 0;
}

原文地址:https://www.cnblogs.com/Zinn/p/10291146.html

时间: 2024-07-31 00:05:49

bzoj 4566 找相同字符 —— 广义后缀自动机的相关文章

[HAOI2016]找相同字符 广义后缀自动机_统计出现次数

题目描述:给定两个字符串,求出在两个字符串中各取出一个子串使得这两个子串相同的方案数.两个方案不同当且仅当这两个子串中有一个位置不同. 输入输出格式输入格式:两行,两个字符串 s1,s2,长度分别为n1,n2.1 <=n1, n2<= 200000,字符串中只有小写字母 输出格式:输出一个整数表示答案 题解:对 $2$ 个字符串建立一个广义后缀自动机.实际上,广义后缀自动机就是对多个字符串用一个自动机加以维护.每加入完毕一个字符串时,将 $last$ 设为 $1$.插入字符时,若 $ch[la

【BZOJ4566】找相同字符(后缀自动机)

[BZOJ4566]找相同字符(后缀自动机) 题面 BZOJ 题解 看到多串处理,\(SA\)就连起来 \(SAM???\) 单串建自动机 然后其他串匹配 对于一个串建完\(SAM\)后 另一个串在\(SAM\)上匹配 记录当前匹配的最大长度 匹配了当前位置的话,就能产生一定的贡献 但是很显然,沿着\(parent\)往上,所有点都能够产生贡献 所以匹配完再沿着\(parent\)做一遍类似\(dp\)的东西算贡献 #include<iostream> #include<cstdio&g

BZOJ 3277 串 (广义后缀自动机)

3277: 串 Time Limit: 10 Sec Memory Limit: 128 MB Submit: 309 Solved: 118 [Submit][Status][Discuss] Description 字符串是oi界常考的问题.现在给定你n个字符串,询问每个字符串有多少子串(不包括空串)是所有n个字符串中至少k个字符串的子串(注意包括本身). Input 第一行两个整数n,k. 接下来n行每行一个字符串. Output 输出一行n个整数,第i个整数表示第i个字符串的答案. Sa

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

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

bzoj3926: [Zjoi2015]诸神眷顾的幻想乡 对广义后缀自动机的一些理解

先说一下对后缀自动机的理解,主要是对构造过程的理解. 构造中,我们已经得到了前L个字符的后缀自动机,现在我们要得到L+1个字符的后缀自动机,什么需要改变呢? 首先,子串$[0,L+1)$对应的状态不存在,应当建立一个状态来表示这个串,显然,这个状态(np)的right集合是{L+1},max=L+1. 现在新建立了一个状态,我们还有两件事要干:找出能转移到这个状态的状态,建立链接:确定这个状态的min,即找到它在parent树上的父亲. 能转移到$np$的状态显然都是right集合包含L的状态,

E. Three strings 广义后缀自动机

http://codeforces.com/problemset/problem/452/E 多个主串的模型. 建立一个广义后缀自动机,可以dp出每个状态的endpos集合大小.同时也维护一个R[]表示那个串出现过. 所以可以算出每个状态的dp[i][k]表示第k个串在第i个状态中出现的次数. 可以知道sigma dp[i][0...k]是等于  endpos集合的大小. 然后把这个贡献加到min(i)....max(i)中去就可以了 差分一下. #include <bits/stdc++.h>

广义后缀自动机

1).自动机的介绍 首先我们先来介绍一下什么是自动机,有限状态自动机的功能是识别字符串,令一个自动机A,若他能识别字符串S,就记为A(S)=Ture,否则A(S)=False. 自动机由五个部分组成,alpha:字符集,state:状态集合,init:初始状态,end:结束状态集合,trans:状态转移函数. 令trans(s,ch)表示当前状态是s,在读入字符ch之后,所到达的状态.如果trans(s,ch)这个转移不存在,为了方便,设其为null,同时null只能转移到null.null表示

【BZOJ4566】找相同字符(后缀数组)

[BZOJ4566]找相同字符(后缀数组) 题面 BZOJ 题解 后缀数组的做法,应该不是很难想 首先看到两个不同的串,当然是接在一起求\(SA,height\) 那么,考虑一下暴力 在两个串各枚举一个后缀,他们的\(lcp\)就是对答案产生的贡献 现在优化一下,按照\(SA\)的顺序枚举来处理\(lcp\) 利用一个单调栈维护一下,每次记录一下前面有多少个的贡献和当前答案一样就好啦 只是有点难写... #include<iostream> #include<cstdio> #in

[HAOI2016]找相同字符(广义SAM)

[HAOI2016]找相同字符(广义SAM) 题面 给定两个字符串,求出在两个字符串中各取出一个子串使得这两个子串相同的方案数.两个方案不同当且仅当这两个子串中有一个位置不同. 分析 此题有一个比较繁琐的后缀数组做法,但是用广义SAM可以秒杀. 把两个串建成广义SAM,对于每个后缀,记录\(endpos\)集合中落在第一个串中和第二个串中的位置个数,记为\(cnt_{x,0},cnt_{x,1}\). 对于自动机上的每个节点\(x\),出现位置方案数的贡献是\(cnt_{x,0} \cdot c