Problem:
给定两个字符串s1和s2,q次查询,每次查询s1中的一段区间[L,R]中有多少个子串在s2中出现过
SOLUTION:
从头开始考虑,我们想知道字串的数量,那我们知道以s1每一个字符为结尾的最长公共字串后,他的所有的后缀都是一个字串
因此
对s2建sam,用s1跑最长公共子串,得到每个位置i往左最多可以匹配到的位置d[i]。显然d[i]是递减。于是对于每个查询,二分得到哪些位置的d[i]是越过L的,这些位置的贡献可以一起得到。剩下的位置的贡献用前缀和维护即可。
CODE:
#include<bits/stdc++.h>
using namespace std;
#define I inline
#define fi first
#define se second
#define pb push_back
#define ALL(X) (X).begin(), (X).end()
#define CLR(A, X) memset(A, X, sizeof(A))
typedef long long LL;
typedef unsigned long long ULL;
typedef pair<int, int> PII;
const int N = 1e5+10, SIGMA = 26;
int d[N];
LL sum[N];
struct SAM {
int last, sz;
int ch[N<<1][SIGMA], len[N<<1], f[N<<1];
void init() {
for(int i = 1; i <= sz; i++) {
CLR(ch[i], 0);
}
last = sz = 1;
f[1] = len[1] = 0;
}
int cal(int p, int c) {
int q = ch[p][c];
if(len[q] == len[p]+1) return q;
int nq = ++sz; len[nq] = len[p]+1;
memcpy(ch[nq], ch[q], sizeof(ch[q]));
f[nq] = f[q]; f[q] = nq;
while(p && ch[p][c]==q) ch[p][c] = nq, p = f[p];
return nq;
}
void insert(int c) {
int p = last;
if(ch[p][c]) last = cal(p, c);
else {
int np = last = ++sz; len[np] = len[p]+1;
while(!ch[p][c] && p) ch[p][c] = np, p = f[p];
if(!p) f[np] = 1;
else f[np] = cal(p, c);
}
}
void solve(char *s) {
int sl = strlen(s+1), u = 1, l = 0;
for(int i = 1; i <= sl; i++) {
int c = s[i]-'a';
while(u!=1 && !ch[u][c]) u = f[u], l = len[u];
if(ch[u][c]) u = ch[u][c], l++;
d[i] = i-l+1;
sum[i] = sum[i-1]+l;
}
}
}A;
char s1[N], s2[N];
I void work() {
A.init();
scanf("%s%s", s1+1, s2);
int len = strlen(s2);
for(int i = 0; i < len; i++) A.insert(s2[i]-'a');
A.solve(s1);
int q; scanf("%d", &q);
static int cas = 0;
printf("Case %d:\n", ++cas);
while(q--) {
int l, r; scanf("%d%d", &l, &r);
int L = l, R = r;
while(L < R) {
int M = (L+R)>>1;
if(d[M] < l) L = M+1;
else R = M;
}
LL ans = 1LL*(L-l)*(L-l+1)/2+sum[r]-sum[L-1];
printf("%lld\n", ans);
}
}
int main() {
if(fopen("curse.in", "r"))
freopen("curse.in", "r", stdin);
int X; scanf("%d", &X);
while(X--) work();
return 0;
}