题解 s2mple
发现这个“在每个本质不同子串中的出现次数十分阴间”
我们可以做一个神奇的转化:
这个东西就是在原串的前后添加一些字符,使得到的串是 \(s\) 的子串的方案数
正确性显然,就是在拼产生贡献的本质不同子串
那么这个问题就比较好解决了
在前面加字符就是 parent 树的子树内的节点
在后面加字符可以在 SAM 形成的 DAG 上做 DP
那么倍增找到 \(s_{l, r}\) 对应的节点统计答案即可
复杂度 \(O(n\log n)\)
点击查看代码
#include <bits/stdc++.h>
using namespace std;
#define INF 0x3f3f3f3f
#define N 1000010
#define fir first
#define sec second
#define pb push_back
#define ll long long
#define ull unsigned long long
//#define int long long
int n, q;
char s[N];
namespace force{
ull h[N], pw[N];
bool vis[2010][2010];
const ull base=13131;
unordered_map<ull, bool> mp;
inline ull hashing(int l, int r) {return h[r]-h[l-1]*pw[r-l+1];}
void solve() {
pw[0]=1;
for (int i=1; i<=n; ++i) pw[i]=pw[i-1]*base;
for (int i=1; i<=n; ++i) {
ull h=0;
for (int j=i; j<=n; ++j) {
h=h*base+s[j];
if (mp.find(h)==mp.end()) vis[i][j]=mp[h]=1;
}
}
for (int i=1,l,r; i<=q; ++i) {
scanf("%d%d", &l, &r);
ull tem=0; ll ans=0;
for (int j=l; j<=r; ++j) tem=tem*base+s[j];
for (int j=1; j<=n; ++j) {
h[j-1]=0; int cnt=0;
for (int k=j; k<=n; ++k) {
h[k]=h[k-1]*base+s[k];
if (k-j>=r-l && hashing(k-(r-l), k)==tem) ++cnt;
if (vis[j][k]) ans+=cnt;
}
}
printf("%lld\n", ans);
}
}
}
namespace task1{
bool vis[N];
ll suf[N], ans[N], dp[N];
vector<pair<int, int>> que[N];
int fail[N], len[N], tr[N][26], cnt[N], tem[N], dep[N], fa[21][N], lg[N], now, tot;
void init() {fail[now=tot=0]=-1;}
void ins(char c) {
c-='a';
int cur=++tot;
len[cur]=len[now]+1;
int p, q;
for (p=now; ~p&&!tr[p][c]; tr[p][c]=cur,p=fail[p]);
if (p==-1) fail[cur]=0;
else if (len[q=tr[p][c]]==len[p]+1) fail[cur]=q;
else {
int cln=++tot;
len[cln]=len[p]+1;
fail[cln]=fail[q];
for (int i=0; i<26; ++i) tr[cln][i]=tr[q][i];
for (; ~p&&tr[p][c]==q; tr[p][c]=cln,p=fail[p]);
fail[cur]=fail[q]=cln;
}
vis[now=cur]=1;
}
void solve() {
init();
for (int i=1; i<=n; ++i) ins(s[i]);
// cout<<"fail: "; for (int i=1; i<=tot; ++i) cout<<fail[i]<<' '; cout<<endl;
// cout<<"len: "; for (int i=1; i<=tot; ++i) cout<<len[i]<<' '; cout<<endl;
for (int i=1; i<=tot; ++i) ++cnt[len[i]];
for (int i=1; i<=n; ++i) cnt[i]+=cnt[i-1];
for (int i=1; i<=tot; ++i) tem[cnt[len[i]]--]=i;
for (int i=1; i<=n; ++i) lg[i]=lg[i-1]+(1<<lg[i-1]==i);
for (int i=tot,u; i; --i) {
++suf[u=tem[i]];
for (int j=0; j<26; ++j) if (tr[u][j]) suf[u]+=suf[tr[u][j]];
// if (vis[u]) suf[u]=dp[u];
// suf[fail[u]]+=suf[u];
dp[fail[u]]+=dp[u]+(len[u]-len[fail[u]])*suf[u];
}
for (int i=1; i<=tot; ++i) dep[tem[i]]=dep[fail[tem[i]]]+1;
for (int i=1,u; i<=tot; ++i) {
u=tem[i];
dep[u]=dep[fa[0][u]=fail[u]]+1;
for (int i=1; dep[u]>=1<<i; ++i) fa[i][u]=fa[i-1][fa[i-1][u]];
}
// cout<<"dp: "; for (int i=1; i<=tot; ++i) cout<<dp[i]<<' '; cout<<endl;
// cout<<"suf: "; for (int i=1; i<=tot; ++i) cout<<suf[i]<<' '; cout<<endl;
for (int i=1,l,r; i<=q; ++i) {
scanf("%d%d", &l, &r);
que[r].pb({l, i});
}
for (int i=1,now=0; i<=n; ++i) {
now=tr[now][s[i]-'a'];
for (auto it:que[i]) {
int u=now;
for (int j=lg[dep[u]]-1; ~j; --j)
if (len[fa[j][u]]>=i-it.fir+1)
u=fa[j][u];
ans[it.sec]=dp[u]+(len[u]-(i-it.fir))*suf[u];
}
}
for (int i=1; i<=q; ++i) printf("%lld\n", ans[i]);
}
}
signed main()
{
freopen("s2mple.in", "r", stdin);
freopen("s2mple.out", "w", stdout);
scanf("%d%d%s", &n, &q, s+1);
// force::solve();
task1::solve();
return 0;
}