对第一个串建一个SAM, 拿第二个上去跑
考虑跑到一个点的贡献, 就是(当前匹配的长度 nowlen - right 集合的最短长度 minlen) * right 集合的大小
因为跟每个长度在这之间的串都可以匹配 siz 次
然后跳 link, 对于它上方的点的贡献, 就是 (maxlen - minlen + 1) * siz
所以我们记录每个点被考虑的次数, 最后向前统计一遍即可
#include<bits/stdc++.h>
#define N 400050
using namespace std;
struct Node{
int ch[26], link, len, siz;
}t[N];
int n, last, tot, c[N], A[N], cnt[N], sum[N]; char s[N];
typedef long long ll; ll ans;
void Extend(int c){
int cur = ++tot, p = last;
t[cur].len = t[p].len + 1; t[cur].siz = 1;
for(;p && !t[p].ch[c]; p = t[p].link) t[p].ch[c] = cur;
if(!p) t[cur].link = 1;
else{
int q = t[p].ch[c];
if(t[q].len == t[p].len + 1) t[cur].link = q;
else{
int clone = ++tot;
t[clone].len = t[p].len + 1;
t[clone].link = t[q].link;
for(int i=0; i<26; i++) t[clone].ch[i] = t[q].ch[i];
for(;p && t[p].ch[c] == q; p = t[p].link) t[p].ch[c] = clone;
t[q].link = t[cur].link = clone;
}
} last = cur;
}
int main(){
scanf("%s", s+1); n = strlen(s+1);
last = tot = 1;
for(int i=1; i<=n; i++) Extend(s[i] - 'a');
for(int i=1; i<=tot; i++) c[t[i].len]++;
for(int i=1; i<=n; i++) c[i] += c[i-1];
for(int i=1; i<=tot; i++) A[c[t[i].len]--] = i;
for(int i=tot; i>=1; i--) t[t[A[i]].link].siz += t[A[i]].siz;
scanf("%s", s+1); n = strlen(s+1);
int pos = 1, nowlen = 0;
for(int i=1; i<=n; i++){
int c = s[i] - 'a';
if(t[pos].ch[c]) pos = t[pos].ch[c], nowlen++;
else{
for(;pos && !t[pos].ch[c]; pos = t[pos].link);
if(!pos) pos = 1, nowlen = 0;
else nowlen = t[pos].len + 1, pos = t[pos].ch[c];
}
ans += 1ll * (nowlen - t[t[pos].link].len) * t[pos].siz; cnt[pos]++; sum[pos]++;
}
for(int i=tot; i>1; i--){
int p = A[i]; sum[t[p].link] += sum[p];
ans += 1ll * (t[p].len - t[t[p].link].len) * t[p].siz * (sum[p] - cnt[p]);
} printf("%lld", ans); return 0;
}