poj3729后缀数组

Posted xiaobuxie

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了poj3729后缀数组相关的知识,希望对你有一定的参考价值。

这题也是从白书上来的。

传送门:https://vjudge.net/problem/POJ-3729

题意:给你a、b串,让你求a串中有多少后缀与b串的所有后缀的公共前缀的长度最大值等于k。

借鉴:https://blog.csdn.net/lj94093/article/details/44703723

其实这题有几点要说的。

1、后缀数组常规做法,把两个串用一个特殊字符连起来,然后这题给的数可能为0,所以要手动+1,也不知道为什么值为0就不行。。。

2、与b的公共前缀长度最大值等于k转换成 num(最大值>=k)-num(最大值>=k+1)。这样转换有什么用呢?最大值>=k,也就是我a的后缀满足与一个b的后缀>=k就可统计了。

3、具体可见代码,看看代码就懂了。我们h数组中小于k的把h数组分成若干段,试想,在同一段中,h都是>=k的,那么如果我们在这一段中有一个b的串,那么这一段中所有的a串都是满足>=k的吧,因为有一个满足即可。

4、第3条分析只是大概的分析,还有点细节,而且这个细节做后缀数组的题经常遇到,就是h(其实就是网上的height数组)数组记录的含义,使得 i 与 j 的最大公共前缀长度是h[i+1] 到h[ j ]。也就是左开右闭。所以加入是 i 到 j这一段中 h 都大于等于k的话,我们还得看看 i-1 那一位的情况,尽管h[ i -1 ] <k。不要忘了第一位。然后就一直扫过去,遇到h<k的就更新一下。

喜闻乐见,上代码。

技术图片
 1 #include<iostream>
 2 #include<cstdio>
 3 using namespace std;
 4 typedef long long ll;
 5 const int N=100000+9;
 6 int s[N],rnk[N],sa[N],h[N],t[N],t2[N],c[N];
 7 void get_sa(int n,int m)
 8     int *x=t,*y=t2;
 9     for(int i=0;i<m;++i) c[i]=0;
10     for(int i=0;i<n;++i) ++c[x[i]=s[i]];
11     for(int i=1;i<m;++i) c[i]+=c[i-1];
12     for(int i=n-1;i>=0;--i) sa[--c[x[i]]]=i;
13     for(int k=1;k<=n;k<<=1)
14         int p=0;
15         for(int i=n-k;i<n;++i) y[p++]=i;
16         for(int i=0;i<n;++i) if(sa[i]-k>=0) y[p++]=sa[i]-k;
17         for(int i=0;i<m;++i) c[i]=0;
18         for(int i=0;i<n;++i) ++c[x[y[i]]];
19         for(int i=1;i<m;++i) c[i]+=c[i-1];
20         for(int i=n-1;i>=0;--i) sa[--c[x[y[i]]]]=y[i];
21         swap(x,y);
22         p=1;x[sa[0]]=0;
23         for(int i=1;i<n;++i) x[sa[i]]=(y[sa[i-1]]==y[sa[i]] && y[sa[i]+k]==y[sa[i-1]+k])?p-1:p++;
24         if(p>=n) break;
25         m=p;
26     
27 
28 void get_h(int n)
29     for(int i=0;i<n;++i) rnk[sa[i]]=i;
30     int k=0;
31     for(int i=0;i<n;++i)
32         if(k) --k;
33         if(rnk[i]==0)
34             k=0;
35             continue;
36         
37         int j=sa[rnk[i]-1];
38         while(s[i+k]==s[j+k]) ++k;
39         h[rnk[i]]=k;
40     
41 
42 ll solve(int k,int n,int l1)
43     ll ans=0;
44     int a=0;
45     bool b=0;
46     if(sa[0]<l1) ++a;
47     if(sa[0]>l1) b=1;
48     for(int i=1;i<n;++i)
49         if(h[i]>=k)
50             if(sa[i]<l1) ++a;
51             if(sa[i]>l1) b=1;
52         
53         else
54             if(b) ans+=a;
55             a=b=0;
56             if(sa[i]<l1) ++a;
57             if(sa[i]>l1) b=1;
58         
59     
60     return ans;
61 
62 int main()
63     int n,m,k;
64     while(~scanf("%d %d %d",&n,&m,&k))
65         for(int i=0;i<n;++i)
66             scanf("%d",&s[i]);
67             ++s[i];
68         
69         s[n]=10000+7;
70         for(int i=n+1;i<n+1+m;++i)
71             scanf("%d",&s[i]);
72             ++s[i];
73         
74         get_sa(n+m+1,10000+8);
75         get_h(n+m+1);
76         printf("%lld\n",solve(k,n+m+1,n)-solve(k+1,n+m+1,n));
77     
78     return 0;
79 
View Code

emm而且也不知道网上的为什么要在str最后一位补一个0.,然后排名变成0到n的了。。。。。。因为我的想法是尽量与模板接近,也就是rnk 和 sa什么的都是 0 到 n-1,然后str最后也不会补0,因为感觉控制好边界就好了。。。。

 

以上是关于poj3729后缀数组的主要内容,如果未能解决你的问题,请参考以下文章

POJ 1743-Musical Theme-后缀数组

POJ 3415 后缀数组

POJ3080 POJ3450Corporate Identity(广义后缀自动机||后缀数组||KMP)

POJ3261(后缀数组+2分枚举)

poj2774 后缀数组

poj2774 后缀数组