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

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

题面

BZOJ

题解

看到多串处理,\(SA\)就连起来
\(SAM???\)
单串建自动机
然后其他串匹配

对于一个串建完\(SAM\)后
另一个串在\(SAM\)上匹配
记录当前匹配的最大长度

匹配了当前位置的话,就能产生一定的贡献
但是很显然,沿着\(parent\)往上,所有点都能够产生贡献
所以匹配完再沿着\(parent\)做一遍类似\(dp\)的东西算贡献

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<set>
#include<map>
#include<vector>
#include<queue>
using namespace std;
#define MAX 220000
inline int read()
{
    int x=0,t=1;char ch=getchar();
    while((ch<'0'||ch>'9')&&ch!='-')ch=getchar();
    if(ch=='-')t=-1,ch=getchar();
    while(ch<='9'&&ch>='0')x=x*10+ch-48,ch=getchar();
    return x*t;
}
struct Node
{
    int son[26];
    int ff,len;
}t[MAX<<1];
int last=1,tot=1;
int size[MAX<<1];
long long ans;
int f[MAX<<1],g[MAX<<1],a[MAX<<1],c[MAX<<1];
char ch[MAX];
void extend(int c)
{
    int p=last,np=++tot;last=np;
    t[np].len=t[p].len+1;
    while(p&&!t[p].son[c])t[p].son[c]=np,p=t[p].ff;
    if(!p)t[np].ff=1;
    else
    {
        int q=t[p].son[c];
        if(t[q].len==t[p].len+1)t[np].ff=q;
        else
        {
            int nq=++tot;
            t[nq]=t[q];
            t[nq].len=t[p].len+1;
            t[q].ff=t[np].ff=nq;
            while(p&&t[p].son[c]==q)t[p].son[c]=nq,p=t[p].ff;
        }
    }
    size[np]=1;
}
int main()
{
    scanf("%s",ch+1);
    for(int i=1,l=strlen(ch+1);i<=l;++i)extend(ch[i]-97);
    for(int i=1;i<=tot;++i)c[t[i].len]++;
    for(int i=1;i<=tot;++i)c[i]+=c[i-1];
    for(int i=1;i<=tot;++i)a[c[t[i].len]--]=i;
    for(int i=tot;i;--i)size[t[a[i]].ff]+=size[a[i]];
    scanf("%s",ch+1);
    for(int i=1,l=strlen(ch+1),now=1,len=0;i<=l;++i)
    {
        int c=ch[i]-97;
        if(t[now].son[c])++len,now=t[now].son[c];
        else
        {
            while(now&&!t[now].son[c])now=t[now].ff;
            if(!now)now=1,len=0;
            else len=t[now].len+1,now=t[now].son[c];
        }
        ans+=1ll*size[now]*(len-t[t[now].ff].len);
        g[now]++;
    }
    for(int i=tot;i;--i)f[t[a[i]].ff]+=f[a[i]]+g[a[i]];
    for(int i=1;i<=tot;++i)ans+=1ll*size[i]*f[i]*(t[i].len-t[t[i].ff].len);
    printf("%lld\n",ans);
    return 0;
}

原文地址:https://www.cnblogs.com/cjyyb/p/8449345.html

时间: 2024-11-06 07:06:20

【BZOJ4566】找相同字符(后缀自动机)的相关文章

HAOI2016 找相同字符 后缀自动机

两个串,考虑一建一跑.枚举模式串的位置\(i\),考虑每次统计以\(i\)结尾的所有符合要求的串.在后缀自动机上走时记录当前匹配长度\(curlen\),则当前节点的贡献是\((curlen-len[f[o]])*rgtsiz[o]\).由蛋糕性质,这个结点在parent树上到根的链也要统计答案,其贡献为\((len[f]-len)*rgtsiz\).实现方法是每次匹配后节点访问次数计数器++,最后自下而上计算贡献. 用广义后缀自动机也可以做. 原文地址:https://www.cnblogs.

[HAOI2016]找相同字符(后缀数组+单调栈)

[HAOI2016]找相同字符(后缀数组+单调栈) 题面 给定两个字符串,求出在两个字符串中各取出一个子串使得这两个子串相同的方案数.两个方案不同当且仅当这两个子串中有一个位置不同. 分析 我们把两个字符串接在一起,中间加一个分隔符.如\(\text{AABB}\)和\(\text{BBAA}\)变成\(\text{AABB|BBAA}\).我们考虑两个相同字串,如\(\text{BB}\),它在新串中对应了两个后缀\(BB|BBAA\)和\(\text{BBAA}\)的LCP. 容易发现,LC

bzoj4566 找相同字符

题意:给定两个字符串,从中各取一个子串使之相同,有多少种取法.允许本质相同. 解:建立广义后缀自动机,对于每个串,分别统计cnt,之后每个点的cnt乘起来.记得开long long 1 #include <cstdio> 2 #include <algorithm> 3 #include <cstring> 4 5 typedef long long LL; 6 const int N = 800010; 7 8 struct Edge { 9 int nex, v;

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

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

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

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

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 co

SPOJ LCS 后缀自动机找最大公共子串

这里用第一个字符串构建完成后缀自动机以后 不断用第二个字符串从左往右沿着后缀自动机往前走,如能找到,那么当前匹配配数加1 如果找不到,那么就不断沿着后缀树不断往前找到所能匹配到当前字符的最大长度,然后将cur节点转移到当前节点即可,再把答案加1 记住不断更新所能得到的最大值 1 #include <cstdio> 2 #include <cstring> 3 #include <iostream> 4 #include <algorithm> 5 using

bzoj4566【HAOI2016】找相同字符

4566: [Haoi2016]找相同字符 Time Limit: 20 Sec  Memory Limit: 256 MB Submit: 128  Solved: 75 [Submit][Status][Discuss] Description 给定两个字符串,求出在两个字符串中各取出一个子串使得这两个子串相同的方案数.两个方案不同当且仅当这两 个子串中有一个位置不同. Input 两行,两个字符串s1,s2,长度分别为n1,n2.1 <=n1, n2<= 200000,字符串中只有小写字

2019年华南理工大学程序设计竞赛(春季赛) K Parco_Love_String(后缀自动机)找两个串的相同字串有多少

https://ac.nowcoder.com/acm/contest/625/K 题意: 给出Q 个询问 i , 求 s[0..i-1] 与 s[i...len-1] 有多少相同的字串 分析: 给出了查询 , 容易想到先预处理出答案好吧 , 字符串的问题也容易想到后缀自动机 ,但是我们该怎么使用呢? 下面提供我的思路: 我们建立出SAM后 , 跑一边拓扑排序 ,根据SAM跑出来的拓扑排序的序列特性 , 我们可以求出 在当前状态st 的最大串字符出现的个数 for (int i = now; i