[HAOI2016]找相同字符(SAM)

题目描述

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

输入输出格式

输入格式:

两行,两个字符串s1,s2,长度分别为n1,n2。1 <=n1, n2<= 200000,字符串中只有小写字母

输出格式:

输出一个整数表示答案

输入输出样例

输入样例#1:
复制

aabb
bbaa

输出样例#1: 复制

10





这到题目是vj上一道题目的简化版对第一个串建立自动机  在拓扑一边球每个状态串的出现次数然后然第二个串在树上跑记录一下当强匹配的长度,假设到达状态P,状态P中的小于匹配长的串都符合,(注意不是P中的所有串),然后就是P的所有的fa都符合,为了避免总是向上找fa而TLE,所以打一个lz标记,最后的时候一边拓扑统计剩下的答案:




  1 #include<iostream>
  2 #include<cstdio>
  3 #include<queue>
  4 #include<map>
  5 #include<set>
  6 #include<queue>
  7 #include<algorithm>
  8 #include<cmath>
  9 #include<stack>
 10 #include<cstring>
 11 #define mem(a,b) memset(a,b,sizeof a)
 12 #define sc(a) scanf("%d",&(a))
 13 #define scc(a,b) scanf("%d %d",&(a),&(b))
 14 #define ll long long
 15 using namespace std;
 16
 17
 18 int kd[1000000];
 19 const int N = 200010;
 20
 21 struct dd
 22 {
 23
 24     int fa,len;
 25     int ch[29];
 26
 27 } dian[1200000];
 28
 29 int cnt[1200000];
 30 ll num[N<<1];
 31 int last=1;
 32 int tot=1;
 33 inline void add(int c)
 34
 35 {
 36     int p=last;
 37     int np=last=++tot;
 38     dian[np].len=dian[p].len+1;
 39     num[np] = 1;
 40     for(; p&&!dian[p].ch[c]; p=dian[p].fa)dian[p].ch[c]=np;
 41     if(!p)dian[np].fa=1,cnt[1]++;
 42     else
 43     {
 44         int q=dian[p].ch[c];
 45         if(dian[q].len==dian[p].len+1)dian[np].fa=q,cnt[q]++;
 46         else
 47         {
 48             int nq=++tot;
 49             dian[nq]=dian[q];
 50             dian[nq].len=dian[p].len+1;
 51             num[nq]=0;
 52             dian[q].fa=dian[np].fa=nq;
 53             cnt[nq]+=2;
 54             for(; p&&dian[p].ch[c]==q; p=dian[p].fa)dian[p].ch[c]=nq;
 55
 56         }
 57     }
 58 }
 59 int ans=0;
 60 vector<int > v;
 61 void top()
 62 {
 63     queue<int >q;
 64     for(int i=2; i<=tot; i++)if(!cnt[i])q.push(i);
 65     while(!q.empty())
 66     {
 67
 68         int t=q.front(); v.push_back(t);
 69         q.pop();
 70         num[dian[t].fa] += num[t];
 71         if(--cnt[dian[t].fa] == 0) q.push(dian[t].fa);
 72     }
 73
 74 }
 75 ll lz[N<<1];
 76
 77 void work(string s,int len)
 78 {
 79     ll ans=0;
 80     int now = 1;  int tt=0;
 81     for(int i=0; i<len; i++)
 82     {
 83         int so=s[i]-‘a‘;
 84         if(dian[now].ch[so])
 85         {
 86             now=dian[now].ch[so]; tt++;
 87             //ans += 1ll*num[now]*(dian[now].len - dian[dian[now].fa].len);
 88           //  lz[dian[now].fa] += 1;
 89         }
 90         else
 91         {
 92             while(now&&!dian[now].ch[so])now=dian[now].fa,tt=dian[now].len;
 93             if(!now)now=1,tt=0;
 94             else
 95             {
 96                 now=dian[now].ch[so]; tt++;
 97                 // ans += 1ll*num[now]*(dian[now].len - dian[dian[now].fa].len);
 98                // lz[dian[now].fa] += 1;
 99             }
100         }
101         if(now!=1)
102         {
103              ans += 1ll*num[now]*(tt- dian[dian[now].fa].len);
104                 lz[dian[now].fa] += 1;
105         }
106
107       //  cout<<"HERE:  "<<now<<" "<<ans<<endl;
108     }
109    // cout<<"NOW: "<<ans<<endl;
110     int sze=v.size();
111     for(int i=0; i<sze; i++)
112     {
113         int t=v[i];
114
115         ans += 1ll*lz[t]*num[t]*(dian[t].len - dian[dian[t].fa].len);
116         lz[dian[t].fa] += lz[t];
117         lz[t]=0;
118     }
119
120     cout<<ans;
121
122
123
124
125 }
126 signed main()
127 {
128     string s;
129     cin>>s;
130     int len = s.length();
131     for(int i=0; i<len; i++)add(s[i]-‘a‘);
132     cin>>s;
133     top();
134     //cout<<"TOT:  "<<tot<<endl;
135    // for(int i=1;i<=tot;i++)cout<<num[i]<<" ";
136    // cout<<endl;
137
138     work(s,s.length());
139
140
141 }


原文地址:https://www.cnblogs.com/zhangbuang/p/10896353.html

时间: 2024-08-30 01:18:20

[HAOI2016]找相同字符(SAM)的相关文章

4566: [Haoi2016]找相同字符 SAM

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

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

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

P3181 [HAOI2016]找相同字符

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

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]找相同字符

题目大意: 给你两个字符串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]找相同字符

题链: 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

luogu3181 [HAOI2016]找相同字符

建广义\(\mathrm{SAM}\),同时对每个串在每个节点处分别维护其\(\mathrm{endpos}\)集合大小,记广义\(\mathrm{SAM}\)上的节点\(u\)的两个串的\(\mathrm{endpos}\)集合大小分别为\(siz_{u,0},siz_{u,1}\),则 \[ \mathrm{Answer}=\sum_{i=1}^{tot} siz_{i,0}\times siz_{i,1} \times (\mathrm{Len}(u)-Len(fa_u)) \] #inc