P3181 [HAOI2016]找相同字符

P3181 [HAOI2016]找相同字符

对一个串建SAM,另一个串在这上面跑,到达一点时,假设经过了\(cnt\)个点
计算这个串所有后缀产生的贡献就好了,直接暴力跑上去可能会超时,topsort预处理一下

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<set>
#include<map>
#include<vector>
#include<queue>
using namespace std;
typedef long long LL;
const LL maxn=500000;
LL nod,last,n,T;
LL len[maxn],fail[maxn],son[maxn][26],Ans[maxn],sum[maxn],c[maxn],p[maxn],size[maxn],fval[maxn];
char s[maxn];
inline void Insert(LL c){
    LL np=++nod,p=last;
    len[np]=len[p]+1;
    last=np;
    while(p&&!son[p][c]){
        son[p][c]=np,
        p=fail[p];
    }
    if(!p)
        fail[np]=1;
    else{
        LL q=son[p][c];
        if(len[q]==len[p]+1)
            fail[np]=q;
        else{
            LL nq=++nod;
            len[nq]=len[p]+1;
            fail[nq]=fail[q];
            memcpy(son[nq],son[q],sizeof(son[q]));
            fail[np]=fail[q]=nq;
            while(p&&son[p][c]==q){
                son[p][c]=nq,
                p=fail[p];
            }
        }
    }
    size[last]=1;
}
int main(){
    nod=last=1;
    scanf("%s",s);
    LL Len=strlen(s);
    for(LL i=0;i<Len;++i)
        Insert(s[i]-'a');

    for(LL i=1;i<=nod;++i)
        c[len[i]]++;
    for(LL i=1;i<=nod;++i)
        c[i]+=c[i-1];
    for(LL i=1;i<=nod;++i)
        p[c[len[i]]--]=i;
    for(LL i=nod;i>=1;--i)
        size[fail[p[i]]]+=size[p[i]];
    for(LL i=1;i<=nod;i++)
        fval[p[i]]=(len[p[i]]-len[fail[p[i]]])*size[p[i]]+fval[fail[p[i]]];

    scanf("%s",s);
    LL now=1,cnt=0,ans=0;
    Len=strlen(s);
    for(LL i=0;i<Len;++i){
        LL c=s[i]-'a';
        if(son[now][c])
            ++cnt,
            now=son[now][c];
        else{
            while(now&&!son[now][c])
                now=fail[now];
            if(!now)
                cnt=0,
                now=1;
            else
                cnt=len[now]+1,
                now=son[now][c];
        }
        if(now!=1)
            ans+=fval[fail[now]]+(cnt-len[fail[now]])*size[now];
    }
    printf("%lld",ans);
    return 0;
}/*
fjewiofejhiofjmwopejeugfzjkjnfoakweldnfmoierhguiewkjfkowejrfoiwejsfd
jwierhdwuiek,dedjfkz[pjeowrfhuqigrfwerljfiuekdfkcdfheosf
*/

原文地址:https://www.cnblogs.com/y2823774827y/p/10202242.html

时间: 2024-07-31 00:08:25

P3181 [HAOI2016]找相同字符的相关文章

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

[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

4566: [Haoi2016]找相同字符 SAM

折腾了好久.不过收获还是很多的.第一次自己去画SAM所建出来fail树.深入体会了这棵树的神奇性质. 当然,我最终靠着自己A掉了.(这是我第一次推SAM的性质(以前都是抄别人的,感觉自己好可耻),不过感觉好像是摸着黑行走啊!) 这道题,可以先对第一个串建出后缀自动机.然后第二个串在后缀自动机上跑. 首先,SAM所建出的fail树的性质有: 1: 树上一个节点对应了多个串,串的个数是 len[x] - len[fa[x]], 同时它们都出现了 sz[x] 次(感觉好像说不大清,可以看一下代码对于s

●BZOJ 4566 [Haoi2016]找相同字符

题链: http://www.lydsy.com/JudgeOnline/problem.php?id=4566 题解: 后缀数组,单调栈.把两个串A,B拼接起来,中间用没出现过的字符隔开.然后用倍增算法求出 sa[] rank[] height[]接着用单调栈维护出两个数组 L[],R[],意义如下:L[i]:表示在后缀数组中,排名最小(记其排名为 L[i])的后缀与排名为 i的后缀的LCP>=hei[i]同理 R[i]:表示在后缀数组中,排名最大(记其排名为 R[i])的后缀与排名为 i的后

BZOJ4566:[HAOI2016]找相同字符——题解

https://www.lydsy.com/JudgeOnline/problem.php?id=4566 https://www.luogu.org/problemnew/show/P3181 给定两个字符串,求出在两个字符串中各取出一个子串使得这两个子串相同的方案数.两个方案不同当且仅当这两个子串中有一个位置不同. 广义后缀自动机,两个串各处理他们的size(或right?),然后对结点l排序,对于每个结点他们的size相乘即为答案. ……等等怎么WA了啊. 比如: aba abaa 这组数

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

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

[HAOI2016]找相同字符(SAM)

题目描述 给定两个字符串,求出在两个字符串中各取出一个子串使得这两个子串相同的方案数.两个方案不同当且仅当这两个子串中有一个位置不同. 输入输出格式 输入格式: 两行,两个字符串s1,s2,长度分别为n1,n2.1 <=n1, n2<= 200000,字符串中只有小写字母 输出格式: 输出一个整数表示答案 输入输出样例 输入样例#1: 复制 aabb bbaa 输出样例#1: 复制 10 这到题目是vj上一道题目的简化版对第一个串建立自动机 在拓扑一边球每个状态串的出现次数然后然第二个串在树上

[HAOI2016]找相同字符

题目大意: 给你两个字符串a和b,要求从a和b中各取出一个相等的子串,问不同的取法有多少种. 思路: 对于a串建立SAM,然后DP求出每个状态Right集合的大小. 然后把b串放进去匹配,对于每一个匹配到的结点p,它的每一个Right状态都可以匹配一个长度为tmp-s[s[p].link].len的串,那么将s[p].right*(tmp-s[s[p].link].len)计入答案. 统计每个状态被匹配的次数,并累加进它Parent中. 将s[top[i]].right*f[top[i]]*(s