HAOI2016找相同字符

bzoj4566 / loj2064

题目

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

做法一:后缀自动机

注意:

  1. 当前匹配到的长度不一定是当前节点的最长长度 所以:在53、57行统计有关当前节点答案的地方,应用 l[f[p]]+1 或是  len-l[f[l]]
  2. 注意拓扑排序的遍历顺序皆为从小到大
 1 #include<cstring>
 2 #include<cstdio>
 3 #include<iostream>
 4 using namespace std;
 5 const int N=600001;
 6 int lst=1,cnt=1,sz[N],l[N],f[N],c[N][27];
 7 inline void ins(int x){
 8     int p=lst,np=++cnt;lst=np;
 9     l[np]=l[p]+1;sz[np]=1;
10     for(;p&&!c[p][x];p=f[p])c[p][x]=np;
11     if(!p)f[np]=1;
12     else{
13         int q=c[p][x];
14         if(l[q]==l[p]+1)f[np]=q;
15         else{
16             int nq=++cnt;
17             f[nq]=f[q];
18             memcpy(c[nq],c[q],sizeof(c[q]));
19             l[nq]=l[p]+1;
20             f[q]=f[np]=nq;
21             for(;p&&c[p][x]==q;p=f[p])c[p][x]=nq;
22         }
23     }
24 }
25 int n1,n2;
26 char s1[N>>1],s2[N>>1];
27 int tmptmp[N],tp[N],sm[N];
28 inline void TP(){
29     for(int i=1;i<=cnt;++i)++tmptmp[l[i]];
30     for(int i=1;i<=n1;++i)tmptmp[i]+=tmptmp[i-1];
31     for(int i=1;i<=cnt;++i)tp[tmptmp[l[i]]--]=i;
32 }
33 long long ans=0;
34 int main(){
35     scanf("%s%s",s1,s2);n1=strlen(s1);n2=strlen(s2);
36     for(int i=0;i<n1;++i){
37         ins(s1[i]-‘a‘);
38     }
39     TP();
40     for(int i=cnt;i>=1;--i)sz[f[tp[i]]]+=sz[tp[i]];
41     for(int i=2;i<=cnt;++i){
42         sm[tp[i]]=sm[f[tp[i]]]+ sz[tp[i]]*(l[tp[i]]-l[f[tp[i]]]);
43     }// for(int i=1;i<=cnt;++i)printf("sm[%d]=%d sz%d f%d tp%d a%d b%d\n",i,l[i],sz[i],f[i],tp[i],c[i][0],c[i][1]);
44     int p=1,len=0,x;
45     for(int i=0;i<n2;++i){//puts("*");
46         x=s2[i]-‘a‘;
47         if(c[p][x]){
48             ++len;p=c[p][x];
49         }else{
50             for(;p&&!c[p][x];p=f[p]);
51             if(!p)len=0,p=1;
52             else{
53                 len=l[p]+1;
54                 p=c[p][x];
55             }
56         }
57         ans+=(long long)(sm[f[p]]+sz[p]*(len-l[f[p]]));
58     }
59     cout<<ans;
60     return 0;
61 }

原文地址:https://www.cnblogs.com/xln1111/p/8710226.html

时间: 2024-10-12 07:06:47

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

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

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的后

[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

bzoj 4566: [Haoi2016]找相同字符

Description 给定两个字符串,求出在两个字符串中各取出一个子串使得这两个子串相同的方案数.两个方案不同当且仅当这两 个子串中有一个位置不同. Input 两行,两个字符串s1,s2,长度分别为n1,n2.1 <=n1, n2<= 200000,字符串中只有小写字母 Output 输出一个整数表示答案 Sample Input aabb bbaa Sample Output 10 HINT Source 首先这种多个字符串的一般要拼成一个串: 如果考虑暴力的话,那就是开头在第一个串的后