首页 写随笔

cdcq(本博客废弃!现用博客:https://www.cnblogs.com/cdcq/)

本博客废弃!现用博客:https://www.cnblogs.com/cdcq/

导航

【BZOJ4566】【HAOI2016】找相同字符

后缀数组有些性质还是比较好用的

原题:

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

1 <=n1, n2<= 200000

 

恩这题不少人都说是广义后缀自动姬

后缀自动姬是很强大,后缀数组也有很多很好的性质,因为我太弱只会后缀数组所以就用后缀数组写了(当然是看题解写的

首先依照后缀数组的惯例,两个串先拼到一起,中间用$连接,后面添一个#(#比$小),然后求出后缀数组和height

使用height的性质,如果两个相邻的后缀属于不同的串(当然其中一个后缀会包含另一个串),他们的height就表示这两个串的最大公共子串

容易想到height性质的扩展,容易发现任意两个后缀(当然不能是#和$开头的)之间最小的height表示这两个后缀的最大公共子串

同时这两个后缀间的所有长度为这两个后缀之间最小height的前缀都是各不相同的子串(是否在同一个串内就不一定了)

酱紫就可以很方便地调用所有公共子串,计算答案就非常容易了

一个方法是按rank递增枚举,对与每次枚举到的i,计temp为height[i],再从i往后枚举j,如果height[j]<temp,temp就更新为height[j],然后为答案加上temp(因为位置不同,所以每个长度为temp的子串对答案的贡献是temp

这个方法是O(N^2)的,4e5会T掉

然后就要使用神奇的并查集了(我感觉并查集这个方法非常不好想,但是用后缀数组拿N^2部分分的话还不如直接kmp

先按height递减拍一下序,然后每次枚举i,把i和i-1合并并计算贡献,设i这个集合中在左边的串有ll[i]个,右边的串有rr[i]个,i和i-1对答案的贡献就是(rr[i]*ll[i-1]+rr[i-1]*ll[i])*height[i]

因为按height递减排序了,就不用考虑height限制的问题了(有点cdq分治的意思

并查集真神奇

代码:

 1 #include<iostream>
 2 #include<cstdio>
 3 #include<algorithm>
 4 #include<cstring>
 5 #include<cmath>
 6 #include<vector>
 7 using namespace std;
 8 int n,m;  char s[410000];  int N;
 9 int tp[410000],ll[410000],rr[410000];
10 int gttp(int x){  while(tp[x]!=x)  x=tp[x];  return x;}
11 void mg(int x,int y){
12     x=gttp(x),y=gttp(y);
13     tp[x]=y,ll[y]+=ll[x],rr[y]+=rr[x];
14 }
15 int rk[410000],hght[410000];
16 int cnt[220],cntrk[410000];
17 int rk1[410000],rk2[410000];
18 int sa[410000],tmpsa[410000];
19 void gtsffxrk(){
20     memset(cnt,0,sizeof(cnt));
21     int i,l;  bool flg;
22     for( i=0;i<N;++i)  ++cnt[s[i]];
23     for( i=1;i<=210;++i)  cnt[i]+=cnt[i-1];
24     for( i=0;i<N;++i)  rk[i]=cnt[s[i]]-1;//为了把$降到0,似乎如果出现#依旧可以使用
25     for( l=1;l<N;l<<=1){
26         for( i=0;i<N;++i)  rk1[i]=rk[i],rk2[i]=(i+l<N)?rk[i+l]:0;
27         fill(cntrk,cntrk+N,0);
28         for( i=0;i<N;++i)  ++cntrk[rk2[i]];
29         for( i=1;i<N;++i)  cntrk[i]+=cntrk[i-1];
30         for( i=N-1;i>=0;--i)  tmpsa[--cntrk[rk2[i]]]=i;
31         fill(cntrk,cntrk+N,0);
32         for( i=0;i<N;++i)  ++cntrk[rk1[i]];
33         for( i=1;i<N;++i)  cntrk[i]+=cntrk[i-1];
34             for( i=N-1;i>=0;--i)  sa[--cntrk[rk1[tmpsa[i]]]]=tmpsa[i];
35             rk[sa[0]]=0;
36         flg=true;
37         for( i=1;i<N;++i){
38             if(rk1[sa[i]]==rk1[sa[i-1]] && rk2[sa[i]]==rk2[sa[i-1]])  rk[sa[i]]=rk[sa[i-1]],flg=false;
39             else  rk[sa[i]]=rk[sa[i-1]]+1;
40         }
41         if(flg)  break;
42     }
43 }
44 void gthght(){
45     int l=0;
46     for(int i=0;i<N;++i)if(rk[i]){
47         int j=sa[rk[i]-1];
48         while(i+l<N && j+l<N && s[i+l]==s[j+l])  ++l;
49         hght[rk[i]]=l;
50         l-=(l>0);
51     }
52 }
53 long long ans=0;
54 void gtans(int x){
55     if(!x)  return ;
56     int r=gttp(x),l=gttp(x-1);
57     ans+=(long long)(ll[r]*rr[l]+rr[r]*ll[l])*hght[x];
58     tp[l]=r,ll[r]+=ll[l],rr[r]+=rr[l];
59 }
60 int a[410000];
61 bool cmp(int x,int y){  return hght[x]>hght[y];}
62 int main(){//freopen("ddd.in","r",stdin);
63     scanf("%s",s);  n=strlen(s);  s[n]='$';
64     scanf("%s",s+n+1);  N=strlen(s);  s[N++]='#';
65     gtsffxrk(),gthght();
66     for(int i=0;i<N;++i){
67         a[i]=tp[i]=i;
68         ll[i]=(sa[i]<n);
69         rr[i]=ll[i]^1;
70     }
71     sort(a,a+N,cmp);
72     for(int i=0;i<N;++i)  gtans(a[i]);
73     cout<<ans<<endl;
74     return 0;
75 }
View Code

 

posted on 2017-02-24 15:21  cdcq_old  阅读(129)  评论(0编辑  收藏  举报