Ciallo~(∠・ω< )⌒★
MutU
·
·
题解
巡厨不请自来~(∠・ω< )⌒★
以下 $n=|s|$,$m=|t|$,数组下标从 $0$ 开始。
要我们求把 $s$ 取一段前缀 $p$ 和一段后缀 $q$,满足 $|p|+|q|<|s|$。求将这两段拼起来后的串中 $t$ 的出现次数之和。
对于完整存在于一段前缀或后缀中的 $t$,我们可以简单求出数量。具体而言,若 $s_{i...i+m-1}=t$,对答案的贡献是 $(1+n-i-m)\times(n-i-m)\div2+i\times(i+1)\div2$。
那么剩下的就是 $t$ 横跨两段的情况。
枚举 $t$ 在 $p$ 中的长度 $len_p$,同时得到 $t$ 在 $q$ 中的长度 $len_q=m-len_p$。
令所有可以作为 $t$ 前 $len_p$ 位左端点的位置的集合为 $A$,可以作为 $t$ 后 $len_q$ 位右端点的位置的集合为 $B$。那么对于 $x\in A$ 和 $y\in B$,如果满足 $y-x\ge|t|$,则贡献一种方案。
此时问题转化为二维偏序,考虑用树状数组维护。
注意到随着 $len_p$ 增大,$A$ 和 $B$ 的变化总数为 $O(n)$ 级别。利用哈希值在每个位置上二分可以求出这个位置往左或往右最多与 $t$ 的前或后若干位相同。然后简单维护修改即可。
```cpp
#include<bits/stdc++.h>
#define int long long
#define ull unsigned long long
using namespace std;
const int N = 400100;
const ull base = 29;
string s,t;
int n,m,ans;
int maxa[N],maxb[N];
vector <int> vea[N],veb[N];
ull p[N],hs[N],ht[N];
int gets(int l,int r){
if(l==0) return hs[r];
return hs[r]-hs[l-1]*p[r-l+1];
}
int gett(int l,int r){
if(l==0) return ht[r];
return ht[r]-ht[l-1]*p[r-l+1];
}
struct BIT{
int tree[N];
int lowbit(int x){
return x & (-x);
}
inline void updata(int x,int d){
int u=x;
while(u<=n){
tree[u]+=d;
u+=lowbit(u);
}
return;
}
int query(int x){
int u=x,ans=0;
while(u>0){
ans+=tree[u];
u-=lowbit(u);
}
return ans;
}
};
BIT A,B;
signed main(){
ios::sync_with_stdio(false);
cin.tie(0); cout.tie(0);
cin>>s>>t;
n=s.size(),m=t.size();
p[0]=1,hs[0]=s[0]-'a'+1,ht[0]=t[0]-'a'+1;
for(int i=1;i<=n+2;i++) p[i]=p[i-1]*base;
for(int i=1;i<n;i++) hs[i]=hs[i-1]*base+(s[i]-'a'+1);
for(int i=1;i<m;i++) ht[i]=ht[i-1]*base+(t[i]-'a'+1);
for(int i=0;i<=n-m;i++){
if(gets(i,i+m-1)==ht[m-1]) ans+=(1+n-i-m)*(n-i-m)/2+i*(i+1)/2;
}
for(int i=0;i<n;i++){
int l=1,r=max(m,n-i);
while(l<=r){
int mid=l+r>>1;
if(gets(i,i+mid-1)==gett(0,mid-1)) maxa[i]=mid,l=mid+1;
else r=mid-1;
}
l=1,r=max(m,i+1);
while(l<=r){
int mid=l+r>>1;
if(gets(i-mid+1,i)==gett(m-mid,m-1)) maxb[i]=mid,l=mid+1;
else r=mid-1;
}
vea[maxa[i]].push_back(i);
veb[maxb[i]].push_back(i);
}
for(int i=0;i<n;i++) A.updata(i+1,1);
for(int i=0;i<veb[m].size();i++) veb[m-1].push_back(veb[m][i]);
int cnt=0;
for(int i=1;i<m;i++){
int j=m-i;
for(int k=0;k<vea[i-1].size();k++){
int u=vea[i-1][k];
if(u+m<=n) cnt-=(B.query(n)-B.query(u+m));
A.updata(u+1,-1);
}
for(int k=0;k<veb[j].size();k++){
int u=veb[j][k];
cnt+=A.query(u-m+1);
B.updata(u+1,1);
}
ans+=cnt;
}
cout<<ans;
return 0;
}
```
###### 但是魔女的夜宴真的很好玩啊。