[bzoj 4566][Haoi 2016]找相同字符

传送门

Description

给定两个字符串,求出在两个字符串中各取出一个子串使得这两个子串相同的方案数。两个方案不同当且仅当这两个子串中有一个位置不同。

Solution

考虑用SAM,采用较为轻松的姿势

首先中加个特殊符号,把连个字符串接在一起

SAM的每个节点都存下了一些子串,我们分别记下它在\(s_1\)和\(s_2\)中出现的次数

那么每个状态它给答案的贡献是
\[
(step[i]-step[fa[i]]) × siz_{s_1}[i]×siz_{s_2}[i]
\]

Code?

//2019.1.25 23:00~23:18
#include<bits/stdc++.h>
#define ll long long
#define max(a,b) ((a)>(b)?(a):(b))
#define min(a,b) ((a)<(b)?(a):(b))
#define MN 200005
char s[MN];
class SAM
{
    #define MX 800005
    private:
        int last,cnt,fa[MX],v[MX],rk[MX],val[MX][2],c[MX][27],step[MX];ll ans;
    public:
        SAM(int _=1):last(_),cnt(_){}
        inline void Insert(int x,bool pos)
        {
            int p=last,np=++cnt;step[np]=step[p]+1;val[np][pos]=1;
            for(;p&&!c[p][x];p=fa[p]) c[p][x]=np;
            if(!p) fa[np]=1;
            else
            {
                int q=c[p][x];
                if(step[q]==step[p]+1) fa[np]=q;
                else
                {
                    int nq=++cnt;
                    fa[nq]=fa[q];fa[np]=fa[q]=nq;
                    memcpy(c[nq],c[q],sizeof c[nq]);
                    step[nq]=step[p]+1;
                    for(;c[p][x]==q;p=fa[p]) c[p][x]=nq;
                }
            }
            last=np;
        }
        inline void Query()
        {
            register int i;
            for(i=1;i<=cnt;++i) ++v[step[i]];
            for(i=1;i<=step[last];++i) v[i]+=v[i-1];
            for(i=1;i<=cnt;++i) rk[v[step[i]]--]=i;
            for(i=cnt;i;--i)
            {
                val[fa[rk[i]]][0]+=val[rk[i]][0];
                val[fa[rk[i]]][1]+=val[rk[i]][1];
                ans+=1ll*val[rk[i]][0]*val[rk[i]][1]*(step[rk[i]]-step[fa[rk[i]]]);
            }
            val[1][0]=val[1][1]=0;
            printf("%lld\n",ans);
        }
}pac;
int main()
{
    scanf("%s",s+1);
    register int n=strlen(s+1),i;
    for(i=1;i<=n;++i) pac.Insert(s[i]-'a',0);
    pac.Insert(26,0);
    scanf("%s",s+1);n=strlen(s+1);
    for(i=1;i<=n;++i) pac.Insert(s[i]-'a',1);
    pac.Query();
    return 0;
}


Blog来自PaperCloud,未经允许,请勿转载,TKS!

原文地址:https://www.cnblogs.com/PaperCloud/p/10322316.html

时间: 2024-10-25 22:00:28

[bzoj 4566][Haoi 2016]找相同字符的相关文章

[HAOI 2016]找相同字符

Description 题库链接 给定两个只含小写字母字符串 \(s_1,s_2\) ,求出在两个字符串中各取出一个子串使得这两个子串相同的方案数.两个方案不同当且仅当这两个子串中有一个位置不同. \(1\leq |s_1|,|s_2|\leq 200000\) Solution 把两个串拼在一起,然后扫两遍. 第一遍统计所有的 \(B\) ,在它前面的 \(A\) 的贡献:第二遍统计所有的 \(A\) ,在它前面的 \(B\) 的贡献: 用单调栈维护一下即可. Code #include <b

BZOJ4566:[Haoi2016]找相同字符

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

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,字符串中只有小写字

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

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

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

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

[BZOJ 2299][HAOI 2011]向量 题解(裴蜀定理)

[BZOJ 2299][HAOI 2011]向量 Description 给你一对数a,b,你可以任意使用(a,b), (a,-b), (-a,b), (-a,-b), (b,a), (b,-a), (-b,a), (-b,-a)这些向量,问你能不能拼出另一个向量(x,y). 说明:这里的拼就是使得你选出的向量之和为(x,y) Input 第一行数组组数t,(t<=50000) 接下来t行每行四个整数a,b,x,y (-2109<=a,b,x,y<=2109) Output t行每行为Y

P3181 [HAOI2016]找相同字符

P3181 [HAOI2016]找相同字符 对一个串建SAM,另一个串在这上面跑,到达一点时,假设经过了\(cnt\)个点 计算这个串所有后缀产生的贡献就好了,直接暴力跑上去可能会超时,topsort预处理一下 #include<iostream> #include<cstdio> #include<cstdlib> #include<cstring> #include<cmath> #include<algorithm> #incl

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

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

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

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