题目链接
题意
给你 n n n 个模式串, q q q 次查询,每次查询 l , r , k l,r,k l,r,k,问第 k k k 个字符串在第 [ l , r ] [l,r] [l,r] 区间的模式串中出现多少次。
思路
首先思考一个模式串
s
s
s 对全部串的贡献如何求?
对
s
s
s 的所有前缀跳 fail 链,对所有经过的模式串终点都有1点贡献,因为当前节点属于
s
s
s 串前缀而经过的模式串终点则是
s
s
s 串后缀。
所以可以,对每个串的经过节点贡献加一,再对fail链倒着建树,在统计s串终止节点的子树贡献。统计子树这个可以dfs序解决。
现在只差如何维护查询区间,这个可以用主席树解决。
代码
#include <bits/stdc++.h>
using namespace std;
#define ll long long
int root[200005], tot;
struct Node {
int l, r, num;
}t[200005*40];
void updata(int &x, int y, int l, int r, int pos, int num) {
t[x = ++tot] = t[y];
if(l == r) {
t[x].num += num;
return;
}
int mid = l+r >> 1;
if(pos <= mid) updata(t[x].l, t[y].l, l, mid, pos, num);
if(pos > mid) updata(t[x].r, t[y].r, mid+1, r, pos, num);
t[x].num = t[t[x].l].num+t[t[x].r].num;
}
int query(int x, int y, int l, int r, int ql, int qr) {
if(ql <= l && r <= qr) return t[y].num-t[x].num;
int mid = l+r >> 1;
int tmp = 0;
if(ql <= mid) tmp += query(t[x].l,t[y].l,l,mid,ql,qr);
if(qr > mid) tmp += query(t[x].r,t[y].r,mid+1,r,ql,qr);
return tmp;
}
/*主席树*/
const int N = 200005;
const int M = 26;
struct ACAM {
int nxt[N][M], fail[N], val[N], n;
int cnt[N], pos[N], tid;
void init() {
memset(nxt,0,sizeof(nxt));
memset(fail,0,sizeof(fail));
memset(val,0,sizeof(val));
memset(cnt,0,sizeof(cnt));
memset(pos,0,sizeof(pos));
n = 0;
tid = 0;
}
void add(char *s) {
int len = strlen(s), now = 0;
for(int i = 0; i < len; ++i) {
int tmp = s[i]-'a';
if(!nxt[now][tmp]) nxt[now][tmp] = ++n;
now = nxt[now][tmp];
}
pos[++tid] = now;
++val[now];
}
void getfail() {
queue<int> q;
for(int i = 0; i < 26; ++i) if(nxt[0][i]) fail[nxt[0][i]] = 0, q.push(nxt[0][i]);
while(!q.empty()) {
int u = q.front(); q.pop();
for(int i = 0; i < 26; ++i) {
if(nxt[u][i]) fail[nxt[u][i]] = nxt[fail[u]][i], q.push(nxt[u][i]);
else nxt[u][i] = nxt[fail[u]][i];
}
}
}
}ac;
vector<int> e[N];
int id, tim[N], sz[N];
string s[N];
char fq[N];
void init() {
id = 0;
tot = 0;
for(int i = 0; i <= ac.n; ++i) e[i].clear();
ac.init();
}
void buildfailtree() {
for(int i = 1; i <= ac.n; ++i) e[ac.fail[i]].push_back(i);
}
void dfs(int u) {
tim[u] = ++id;
sz[u] = 1;
for(auto v : e[u]) dfs(v), sz[u] += sz[v];
}
void up(int &x, int y) {
t[x = ++tot] = t[y];
int len = strlen(fq), now = 0;
for(int i = 0; i < len; ++i) {
int nxt = ac.nxt[now][fq[i]-'a'];
updata(x,x,1,id,tim[nxt],1);
now = nxt;
}
}
int main() {
int n, q;
scanf("%d%d",&n,&q);
init();
for(int i = 1; i <= n; ++i) {
cin >> s[i];
int len = s[i].length();
for(int j = 0; j < len; ++j) fq[j] = s[i][j];
fq[len] = 0;
ac.add(fq);
}
ac.getfail();
buildfailtree();
dfs(0);
for(int i = 1; i <= n; ++i) {
int len = s[i].length();
for(int j = 0; j < len; ++j) fq[j] = s[i][j];
fq[len] = 0;
up(root[i],root[i-1]);
}
// printf("%d %d -!!!!! %d\n",tim[ac.pos[1]],sz[ac.pos[1]],tim[ac.pos[5]]);
while(q--) {
int l, r, k;
scanf("%d%d%d",&l,&r,&k);
k = ac.pos[k];
printf("%d\n",query(root[l-1],root[r],1,id,tim[k],tim[k]+sz[k]-1));
}
return 0;
}