题解 [CF547E] Mike and Friends
把 call 函数的定义读反了,自闭了一年
读反了其实也是可以做的
那么问题变为:给定串 ,对于一个 求 作为子串在 中出现的次数
做法 1:
因为我啥也不会嘛
对 建广义 SAM,考虑子串是前缀的后缀
那么把 在上面跑,对于每个前缀对应的节点对其到祖先的路径上 对应某个/些 的节点个数 求和
需要特殊处理这个前缀对应的节点对应某个/些 的情况
对于 的限制应该可以主席树
所谓“特殊处理”:没有 限制应该就是对每个节点开 set 存对应的 s 的长度,查询时二分 的
加上限制就是各种二维偏序了
upd:哦我好像傻了,每个节点貌似最多对应一个
做法 2:
这个“特殊处理”很烦,设法去掉它
那么对 建 SAM,把所有 拿来在上面跑
每个 对应的节点产生的贡献是这个节点 endpos 集合的大小
加限制的话应该可以差分,跑 前加入 ,跑完 删除
回到原题:
现在求包含 的次数
发现 AC 自动机更好用,差分成 和 处理
若 的每个前缀对对应节点产生 1 贡献,就是求 fail 树上子树贡献和
树状数组即可
点击查看代码
#include <bits/stdc++.h>
using namespace std;
#define INF 0x3f3f3f3f
#define N 500010
#define fir first
#define sec second
#define pb push_back
#define ll long long
//#define int long long
int n, q;
char *s[N], t[N];
ll bit[N], ans[N];
int head[N], ecnt;
vector<pair<int, int>> add[N], del[N];
int tr[N][26], fail[N], siz[N], id[N], pos[N], tot, now;
struct edge{int to, next;}e[N];
inline void link(int s, int t) {e[++ecnt]={t, head[s]}; head[s]=ecnt;}
inline void upd(int i, int dat) {for (; i<=now; i+=i&-i) bit[i]+=dat;}
inline ll query(int i) {ll ans=0; for (; i; i-=i&-i) ans+=bit[i]; return ans;}
int ins(char* c) {
int p, *t;
for (p=0; ; p=*t) {
t=&tr[p][*c-'a'];
if (!*t) *t=++tot;
if (!*(++c)) return *t;
}
}
void dfs(int u) {
siz[u]=1;
id[u]=++now;
for (int i=head[u]; ~i; i=e[i].next)
dfs(e[i].to), siz[u]+=siz[e[i].to];
}
void build() {
int u=0;
queue<int> q;
for (int i=0; i<26; ++i)
if (tr[u][i]) fail[tr[u][i]]=u, link(u, tr[u][i]), q.push(tr[u][i]);
else tr[u][i]=u;
while (q.size()) {
u=q.front(); q.pop();
for (int i=0; i<26; ++i)
if (tr[u][i]) fail[tr[u][i]]=tr[fail[u]][i], link(tr[fail[u]][i], tr[u][i]), q.push(tr[u][i]);
else tr[u][i]=tr[fail[u]][i];
}
}
void enable(char* c) {
int p, *t;
for (p=0; ; p=*t) {
t=&tr[p][*c-'a'];
upd(id[*t], 1);
if (!*(++c)) return ;
}
}
signed main()
{
// cout<<double(sizeof(tr))/1000/1000<<endl;
scanf("%d%d", &n, &q);
memset(head, -1, sizeof(head));
for (int i=1,len; i<=n; ++i) {
scanf("%s", t+1);
len=strlen(t+1);
s[i]=new char[len+5];
for (int j=1; j<=len+1; ++j) s[i][j]=t[j];
pos[i]=ins(s[i]+1);
}
build(); dfs(0);
for (int i=1,l,r,k; i<=q; ++i) {
scanf("%d%d%d", &l, &r, &k);
add[r].pb({k, i});
del[l-1].pb({k, i});
}
for (int i=1; i<=n; ++i) {
enable(s[i]+1);
for (auto it:add[i]) ans[it.sec]+=query(id[pos[it.fir]]+siz[pos[it.fir]]-1)-query(id[pos[it.fir]]-1);
for (auto it:del[i]) ans[it.sec]-=query(id[pos[it.fir]]+siz[pos[it.fir]]-1)-query(id[pos[it.fir]]-1);
}
for (int i=1; i<=q; ++i) printf("%lld\n", ans[i]);
return 0;
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· 阿里巴巴 QwQ-32B真的超越了 DeepSeek R-1吗?
· 【译】Visual Studio 中新的强大生产力特性
· 【设计模式】告别冗长if-else语句:使用策略模式优化代码结构
· AI与.NET技术实操系列(六):基于图像分类模型对图像进行分类