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 }

时间: 2024-08-16 11:24:03

3230: 相似子串的相关文章

BZOJ 3230: 相似子串( RMQ + 后缀数组 + 二分 )

二分查找求出k大串, 然后正反做后缀数组, RMQ求LCP, 时间复杂度O(NlogN+logN) --------------------------------------------------------------------- #include<cstdio> #include<algorithm> #include<cstring> #include<cctype> using namespace std; typedef long long

BZOJ 3230 相似子串 | 后缀数组 二分 ST表

BZOJ 3230 相似子串 题面 题解 首先我们要知道询问的两个子串的位置. 先正常跑一遍后缀数组并求出height数组. 对于每一个后缀suffix(i),考虑以i开头的子串有多少是之前没有出现过的,也就是考虑左端点在i.右端点在什么范围内时这个子串没有出现过--答案是右端点在[i + height[i] - 1, n]范围内时这个子串没出现过,即右端点在没有被"i与排在前一个的后缀的公共前缀"覆盖的部分时,这个子串没有出现过. 那么我们记录以每个i开头的新子串的数量,求前缀和,然

bzoj 3230 相似子串——后缀数组

题目:https://www.lydsy.com/JudgeOnline/problem.php?id=3230 作出后缀数组,从 LCP 看每个位置对于本质不同子串的贡献,而且他们已经按前面部分排好序了,所以直接在 sa[ ] 上二分就能找到询问的子串. 找最长公共前缀就用 ht[ ] 和子串的长度比较就行.找最长公共后缀就一开始把原串翻转,做出翻转后的 ht[ ] ,就能查询了. 也可以二分一个最长公共后缀的位置,然后用正常的 ht[ ] 判断. 注意 long long . #includ

bzoj 3230: 相似子串【SA+st表+二分】

总是犯低级错误,st表都能写错-- 正反分别做一遍SA,预处理st表方便查询lcp,然后处理a[i]表示前i个后缀一共有多少个本质不同的子串,这里的子串是按字典序的,所以询问的时候直接在a上二分排名就能得到询问区间,然后用正反st表查lcp即可 #include<iostream> #include<cstdio> #include<algorithm> using namespace std; const int N=200005; int n,q,b[N],sa1[

LeetCode 30 Substring with Concatenation of All Words(与所有文字串联子串)(*)

翻译 给定一个字符串S,一个单词的列表words,全是相同的长度. 找到的子串(多个)以s即每个词的字串联恰好一次并没有任何插入的字符所有的起始索引. 原文 You are given a string, s, and a list of words, words, that are all of the same length. Find all starting indices of substring(s) in s that is a concatenation of each word

最长公共子串

(连续) - 阿里笔试[分析+编码] 题目描述:给定一个query和一个text,均由小写字母组成.要求在text中找出以同样的顺序连续出现在query中的最长连续字母序列的长度.例如,query为“acbac”,text为“acaccbabb”,那么text中的“cba”为最长的连续出现在query中的字母序列,因此,返回结果应该为其长度3.请注意程序效率. [思路]用一个矩阵来记录两个字符串中所有位置的两个字符之间的匹配情况,若是匹配则为1,否则为0.然后求出对角线最长的1序列,其对应的位置

HDU4622:Reincarnation(后缀数组,求区间内不同子串的个数)

Problem Description Now you are back,and have a task to do: Given you a string s consist of lower-case English letters only,denote f(s) as the number of distinct sub-string of s. And you have some query,each time you should calculate f(s[l...r]), s[l

c之PAT刷题---删除字符串中的特定子串

改了好久还是没有全过,等明天再看吧!好好休息,明天继续. #include<stdio.h> #include<string.h> char *delete(char str1[80],char str2[80]){ int len1,len2,l;//l记录出现字符相同的起始位置 len1=strlen(str1); len2=strlen(str2); for(int i=0;str1[i]!='\0';i++){ if(str1[i]==str2[0]){ l=i;//记下相

寻找最大子串

题目:从一个数组中寻找一个连续子数组,使其中元素之和最大,给出该和值. 例如:数组[-2, 1, -3, 4, -1, 2, 1, -5, 4]和最大的子串是[4, -1, 2, 1],其和为6. 分析: 将数组从中间切分,中间元素属于左边,则最大子串为下面三种情况之一: 子串仅包含左边元素: 子串仅包含右边元素: 子串同时包含左边和右边的元素: 代码: // JavaScript solution function findMaxSubArray(a, begin, end) {   if(b