首先进行字符串哈希(注意卡常)。

然后考虑一个字符串对怎么样的区间进行贡献,是一个二维前缀和问题。

  • $L \in [1,l],\ R \in [r,n]$ 加一。
  • $L \in [1,l’],\ R \in [r,n]$ 减一。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
#include<bits/stdc++.h>
using namespace std;
#define ull unsigned long long
const int maxn=3000;
const int maxm=maxn*maxn;
const ull base=20090327;
int n,m;
char str[maxn+5];
ull sm[maxn+5],pw[maxn+5];
int id[maxn+5][maxn+5],tot;
unordered_map<ull,int> mp;
int lst[maxm+5];
int ans[maxn+5][maxn+5];
inline ull Hash(int l,int r){
return sm[r]-sm[l-1]*pw[r-l+1];
}
signed main(){
freopen("substring.in","r",stdin);
freopen("substring.out","w",stdout);
scanf("%d%d",&n,&m);
scanf("%s",str+1);
pw[0]=1;
for(int i=1;i<=n;i++){
sm[i]=base*sm[i-1]+str[i];
pw[i]=base*pw[i-1];
}
for(int k=1;k<=n;k++){
mp.clear();
for(int i=1,j=k;j<=n;i++,j++){
ull tmp=Hash(i,j);
if(!mp[tmp]) mp[tmp]=++tot;
id[i][j]=mp[tmp];
}
}
for(int k=1;k<=n;k++){
for(int i=1,j=k;j<=n;i++,j++){
ans[i][j]++;
ans[lst[id[i][j]]][j]--;
lst[id[i][j]]=i;
}
}
for(int i=n;i>=1;i--)
for(int j=i;j<=n;j++)
ans[i][j]+=ans[i][j-1];
for(int j=1;j<=n;j++)
for(int i=j;i>=1;i--)
ans[i][j]+=ans[i+1][j];
int l,r;
while(m--){
scanf("%d%d",&l,&r);
printf("%d\n",ans[l][r]);
}
return 0;
}