[BZOJ3230] 相似字串 后缀数组+RMQ

3230: 相似子串

Time Limit: 20 Sec  Memory Limit: 128 MB

Description

Input

输入第1行,包含3个整数N,Q。Q代表询问组数。
第2行是字符串S。
接下来Q行,每行两个整数i和j。(1≤i≤j)。

Output

输出共Q行,每行一个数表示每组询问的答案。如果不存在第i个子串或第j个子串,则输出-1。

Sample Input

5 3
ababa
3 5
5 9
8 10

Sample Output

18
16
-1

HINT

样例解释

第1组询问:两个子串是“aba”,“ababa”。f = 32 + 32 = 18。

第2组询问:两个子串是“ababa”,“baba”。f = 02 + 42 = 16。

第3组询问:不存在第10个子串。输出-1。数据范围

N≤100000,Q≤100000,字符串只由小写字母'a'~'z'组成 

 

题解: 首先我们要解决的是本质不同的子串计数问题:

考虑到子串一定是后缀的前缀,我们按照rank顺序添加每个后缀,

那么每添加一个新后缀就会产生n-sa+1个新的前缀(子串)

但是由于lcp的存在,这些子串又和前面的那一串重复的一些

所以最后的计算式是Σn-sa+1-height,当然具体的细节,诸如±1会随代码风格和计算方式略有不同,读者自行修改即可

接着我们考虑,本题其实就是让我们求某两个子串最长公共前缀和最长公共后缀

这样我们可以跑一个SA之后把字串反转再求一套,我们就得到了后缀数组和一个诡异的"前缀数组"

接着我们二分找到子串对应的端点以及后缀,再用rmq求一下lcp区间最小值即可.

代码实现(当时我调到意识模糊于是封装了一下233):

 1 #include <cstdio>
 2 #include <cstring>
 3 #include <algorithm>
 4 using namespace std;
 5 typedef long long LL;
 6 const int N=100010;
 7 int n,xx[N],yy[N],cnt[N],bin[23];
 8 LL num[N];
 9 struct Fleet
10 {
11     int sa[N],height[N],rank[N],f[N][18];
12     char s[N];
13     int i,j,k,p,m;
14     inline void get_sa()
15     {
16         int *x=xx,*y=yy;m=256;
17         for(i=0;i<m;++i)cnt[i]=0;
18         for(i=0;i<n;++i)++cnt[x[i]=s[i]];
19         for(i=1;i<m;++i)cnt[i]+=cnt[i-1];
20         for(i=n-1;~i;--i)sa[--cnt[x[i]]]=i;
21         for(k=1,p=0;p<n&&k<=n;k<<=1,m=p)
22         {
23             for(p=0,i=n-k;i<n;++i)y[p++]=i;
24             for(i=0;i<n;++i)if(sa[i]>=k)y[p++]=sa[i]-k;
25             for(i=0;i<m;++i)cnt[i]=0;
26             for(i=0;i<n;++i)++cnt[x[y[i]]];
27             for(i=1;i<m;++i)cnt[i]+=cnt[i-1];
28             for(i=n-1;~i;--i)sa[--cnt[x[y[i]]]]=y[i];
29             for(swap(x,y),p=1,x[sa[0]]=0,i=1;i<n;++i)
30                 x[sa[i]]=(y[sa[i]]==y[sa[i-1]]&&y[sa[i]+k]==y[sa[i-1]+k])?p-1:p++;
31         }
32     }
33     inline void get_rank()
34     {
35         for(i=0;i<n;++i)rank[sa[i]]=i;
36         for(k=i=0;i<n;height[rank[i++]]=k)
37             for(k=k?k-1:k,j=sa[rank[i]-1];s[i+k]==s[j+k];++k);
38     }
39     inline void ST()
40     {
41         for(i=0;i<n;++i)f[i][0]=height[i];
42         for(i=1;bin[i]<=n;++i)
43             for(j=0;j+bin[i]-1<n;++j)
44                 f[j][i]=min(f[j][i-1],f[j+bin[i-1]][i-1]);
45     }
46     inline LL query(int l,int r)
47     {
48         int len=r-l+1,k=0;
49         while(bin[k+1]<=len)++k;
50         return min(f[l][k],f[r-bin[k]+1][k]);
51     }
52     inline void get_kth(int &st,int &ed,LL rk)
53     {
54         int ans=lower_bound(num,num+n,rk)-num;
55         st=sa[ans],ed=st+height[ans]-1+rk-num[ans-1];
56     }
57     inline void intn()
58         {get_sa(),get_rank(),ST();}
59     inline void calc()
60         {for(i=0;i<n;++i)num[i]=num[i-1]+LL(n-sa[i]-height[i]-1);}
61 }Sfx,Pre;
62 inline LL get_length(LL id1,LL id2)
63 {
64     register int i,j,k,st[2],ed[2];
65     Sfx.get_kth(st[0],ed[0],id1);
66     Sfx.get_kth(st[1],ed[1],id2);
67     LL val=min(ed[0]+1ll-st[0],ed[1]+1ll-st[1]),tmp=val;
68     if(st[0]!=st[1])
69         if(Sfx.rank[st[0]]<Sfx.rank[st[1]])
70             tmp=min(tmp,Sfx.query(Sfx.rank[st[0]]+1,Sfx.rank[st[1]]));
71         else
72             tmp=min(tmp,Sfx.query(Sfx.rank[st[1]]+1,Sfx.rank[st[0]]));
73     ed[0]=n-2-ed[0],ed[1]=n-2-ed[1];
74     if(ed[0]!=ed[1])
75         if(Pre.rank[ed[0]]<Pre.rank[ed[1]])
76             val=min(val,Pre.query(Pre.rank[ed[0]]+1,Pre.rank[ed[1]]));
77         else
78             val=min(val,Pre.query(Pre.rank[ed[1]]+1,Pre.rank[ed[0]]));
79     return tmp*tmp+val*val;
80 }
81 int main()
82 {
83     register int i,j,m,a,b,q;LL u,v;
84     scanf("%d%d%s",&n,&m,Sfx.s);
85     for(bin[0]=i=1;i<=20;++i)bin[i]=bin[i-1]<<1;
86     for(i=1;i<=n;++i)Pre.s[i-1]=Sfx.s[n-i];
87     Sfx.s[n]=Pre.s[n]=1,n++;
88     Sfx.intn();Pre.intn();Sfx.calc();
89     while(m--)
90     {
91         scanf("%lld%lld",&u,&v);
92         if(u>num[n-1]||v>num[n-1])printf("-1\n");
93         else printf("%lld\n",get_length(u,v));
94     }
95 }

 

posted @ 2017-10-01 19:58  LadyLex  阅读(267)  评论(0编辑  收藏  举报