3230: 相似子串

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'组成

 
建立后缀数组,h和pr数组。。。
查询字典序为i的串,我们知道以i开头的不同的字串是n-h[i]-sa[i]那么就可以二分的寻找i,
然后用线段树或者st算法预处理h,pr数组,然后每次查询即可。。。
代码来自黄学长。。
 1 #include<map>
 2 #include<set>
 3 #include<cstdio>
 4 #include<cstring>
 5 #include<vector>
 6 #include<cstdlib>
 7 #include<iostream>
 8 #include<algorithm>
 9 #define inf 1000000000
10 #define ll long long
11 using namespace std;
12 ll read()
13 {
14     ll x=0,f=1;char ch=getchar();
15     while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
16     while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
17     return x*f;
18 }
19 int n,m,Log[100005];
20 char ch[100005];
21 struct SA{
22     int p,q,k;
23     int sa[2][100005],rk[2][100005],mn[17][100005];
24     int a[100005],h[100005],v[100005];ll s[100005];
25     SA(){
26         p=0,q=1;
27     }
28     void mul(int *sa,int *rk,int *SA,int *RK){
29         for(int i=1;i<=n;i++)v[rk[sa[i]]]=i;
30         for(int i=n;i;i--)
31             if(sa[i]>k)SA[v[rk[sa[i]-k]]--]=sa[i]-k;
32         for(int i=n-k+1;i<=n;i++)SA[v[rk[i]]--]=i;
33         for(int i=1;i<=n;i++)
34             RK[SA[i]]=RK[SA[i-1]]+(rk[SA[i-1]]!=rk[SA[i]]||rk[SA[i-1]+k]!=rk[SA[i]+k]);
35     }
36     void getsa(){
37         for(int i=1;i<=n;i++)v[a[i]]++;
38         for(int i=1;i<=30;i++)v[i]+=v[i-1];
39         for(int i=1;i<=n;i++)sa[p][v[a[i]]--]=i;
40         for(int i=1;i<=n;i++)
41             rk[p][sa[p][i]]=rk[p][sa[p][i-1]]+(a[sa[p][i]]!=a[sa[p][i-1]]);
42         for(k=1;k<n;k<<=1,swap(p,q))
43             mul(sa[p],rk[p],sa[q],rk[q]);
44         for(int k=0,i=1;i<=n;i++)
45         {
46             int j=sa[p][rk[p][i]-1];
47             while(a[i+k]==a[j+k])k++;
48             h[rk[p][i]]=k;if(k)k--;            
49         }
50     }
51     void pre(){
52         for(int i=1;i<=n;i++)a[i]=ch[i]-'a'+1;
53         getsa();
54         for(int i=1;i<=n;i++)mn[0][i]=h[i];
55         for(int i=1;i<=Log[n];i++)
56             for(int j=1;j<=n;j++)
57                 if(j+(1<<i)-1<=n)
58                     mn[i][j]=min(mn[i-1][j],mn[i-1][j+(1<<(i-1))]);
59                 else break;
60         for(int i=1;i<=n;i++)
61             s[i]=s[i-1]+n-sa[p][i]+1-h[i];
62     }
63     int query(int a,int b){
64         a=rk[p][a],b=rk[p][b];
65         if(a>b)swap(a,b);a++;
66         int t=Log[b-a+1];
67         return min(mn[t][a],mn[t][b-(1<<t)+1]);
68     }
69     void print(){
70         for(int i=2;i<=n;i++)printf("%d ",h[i]);
71     }
72 }A,B;
73 int main()
74 {
75     Log[0]=-1;for(int i=1;i<=100000;i++)Log[i]=Log[i>>1]+1;
76     n=read();m=read();
77     scanf("%s",ch+1);A.pre();
78     reverse(ch+1,ch+n+1);B.pre();
79     for(int i=1;i<=m;i++)
80     {
81         ll l=read(),r=read(),id,a1,a2,b1,b2,ans=0;
82         if(l>A.s[n]||r>A.s[n]){puts("-1");continue;}
83         id=lower_bound(A.s+1,A.s+n+1,l)-A.s;
84         a1=A.sa[A.p][id];
85         b1=A.sa[A.p][id]+A.h[id]-1+l-A.s[id-1];
86         id=lower_bound(A.s+1,A.s+n+1,r)-A.s;
87         a2=A.sa[A.p][id];
88         b2=A.sa[A.p][id]+A.h[id]-1+r-A.s[id-1];
89         ll t=(a1==a2)?inf:A.query(a1,a2);
90         t=min(t,min(b1-a1+1,b2-a2+1));ans+=t*t;
91         t=(n-b1+1==n-b2+1)?inf:B.query(n-b1+1,n-b2+1);
92         t=min(t,min(b1-a1+1,b2-a2+1));ans+=t*t;
93         printf("%lld\n",ans);
94     }
95     return 0;
96 }
View Code

 

转载于:https://www.cnblogs.com/htwx/articles/5554025.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值