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;
 10 }edge[N << 1]; int top;
 11
 12 int tr[N][26], len[N], fail[N], cnt[N][2], vis[N];
 13 int tot = 1, last, turn, e[N];
 14 char s[N], str[N];
 15
 16 inline void add(int x, int y) {
 17     top++;
 18     edge[top].v = y;
 19     edge[top].nex = e[x];
 20     e[x] = top;
 21     return;
 22 }
 23
 24 inline int split(int p, int f) {
 25     int Q = tr[p][f], nQ = ++tot;
 26     len[nQ] = len[p] + 1;
 27     fail[nQ] = fail[Q];
 28     fail[Q] = nQ;
 29     memcpy(tr[nQ], tr[Q], sizeof(tr[Q]));
 30     while(tr[p][f] == Q) {
 31         tr[p][f] = nQ;
 32         p = fail[p];
 33     }
 34     return nQ;
 35 }
 36
 37 inline int insert(int p, char c) {
 38     int f = c - ‘a‘;
 39     if(tr[p][f]) {
 40         int Q = tr[p][f];
 41         if(len[Q] == len[p] + 1) {
 42             cnt[Q][turn] = 1;
 43             return Q;
 44         }
 45         int t = split(p, f);
 46         cnt[t][turn] = 1;
 47         return t;
 48     }
 49     int np = ++tot;
 50     len[np] = len[p] + 1;
 51     cnt[np][turn] = 1;
 52     while(p && !tr[p][f]) {
 53         tr[p][f] = np;
 54         p = fail[p];
 55     }
 56     if(!p) {
 57         fail[np] = 1;
 58     }
 59     else {
 60         int Q = tr[p][f];
 61         if(len[Q] == len[p] + 1) {
 62             fail[np] = Q;
 63         }
 64         else {
 65             fail[np] = split(p, f);
 66         }
 67     }
 68     return np;
 69 }
 70
 71 void DFS(int x) {
 72     for(int i = e[x]; i; i = edge[i].nex) {
 73         int y = edge[i].v;
 74         DFS(y);
 75         cnt[x][0] += cnt[y][0];
 76         cnt[x][1] += cnt[y][1];
 77     }
 78     return;
 79 }
 80
 81 int main() {
 82     scanf("%s%s", s, str);
 83     int n = strlen(s), last = 1;
 84     for(int i = 0; i < n; i++) {
 85         last = insert(last, s[i]);
 86     }
 87     n = strlen(str);
 88     last = turn = 1;
 89     for(int i = 0; i < n; i++) {
 90         last = insert(last, str[i]);
 91     }
 92     for(int i = 2; i <= tot; i++) {
 93         add(fail[i], i);
 94     }
 95     DFS(1);
 96     int p = 1;
 97     LL ans = 0;
 98     for(int i = 2; i <= tot; i++) {
 99         ans += 1ll * cnt[i][0] * cnt[i][1] * (len[i] - len[fail[i]]);
100     }
101
102     printf("%lld", ans);
103     return 0;
104 }

AC代码

原文地址:https://www.cnblogs.com/huyufeifei/p/10234620.html

时间: 2024-11-06 09:45:58

bzoj4566 找相同字符的相关文章

【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

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

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

BZOJ4566 [Haoi2016]找相同字符 【后缀数组】

题目 给定两个字符串,求出在两个字符串中各取出一个子串使得这两个子串相同的方案数.两个方案不同当且仅当这两 个子串中有一个位置不同. 输入格式 两行,两个字符串s1,s2,长度分别为n1,n2.1 <=n1, n2<= 200000,字符串中只有小写字母 输出格式 输出一个整数表示答案 输入样例 aabb bbaa 输出样例 10 题解 先考虑暴力怎么做 我们枚举两个串的各自一个后缀suffix(i)和suffix(j) 则他们对答案的贡献是LCP(suffix(i),suffix(j)) 如

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 这组数