题目
题意
n
(
1
e
6
)
n(1e6)
n(1e6)个询问,
k
k
k个模式串(总长度
(
1
e
6
)
(1e6)
(1e6))。
询问:
1:给定串
s
s
s求,
s
s
s串里模式串出现几次。
2:将第
i
i
i个串删除(若已经删除无视此操作。
2:将第
i
i
i个串恢复(若已经存在无视此操作。
思路
建立AC自动机,在fail树上,用当前结点去更新后代。
可以在fail树上先序
d
f
s
序
dfs序
dfs序上维护线段树(区间更新,单点操作,此功能可以用树状数组差分维护)。
将所有模式串尾的结点
v
i
v_i
vi,
[
d
f
n
(
v
i
)
,
d
f
s
(
v
i
)
+
s
o
n
(
v
i
)
−
1
]
[dfn(v_i),dfs(v_i)+son(v_i)-1]
[dfn(vi),dfs(vi)+son(vi)−1]这一段+1,表示这个模式串作为后缀对其他结点的贡献。
询问时,逐位询问,每次询问到一个结点,得到的就是所有模式串可以作为当前后缀个数,每次都加上当前后缀模式串个数。
(一顿口胡
线段树:
/* Author : Rshs
* Data : 2019-10-07-20.20
*/
#include<bits/stdc++.h>
using namespace std;
#define FI first
#define SE second
#define LL long long
#define MP make_pair
#define PII pair<int,int>
#define SZ(a) (int)a.size()
const double pai = acos(-1);
const double eps = 1e-10;
const LL mod = 1e9+7;
const int MXN = 1e6+5;
/*******segTree******************************************************************/
int lz[MXN<<2],T[MXN<<2];
inline void pushUp(int rt){
T[rt]=T[rt<<1]+T[rt<<1|1];
}
void pushDown(int rt,int le){
if(lz[rt]){
lz[rt<<1]+=lz[rt];
lz[rt<<1|1]+=lz[rt];
T[rt<<1]+=lz[rt]*(le-(le>>1));
T[rt<<1|1]+=lz[rt]*(le>>1);
lz[rt]=0;
}
}
void build(int l,int r,int rt){
lz[rt]=0;
if(l==r){
T[rt]=0;
return;
}
int m=(l+r)>>1;
build(l,m,rt<<1);
build(m+1,r,rt<<1|1);
pushUp(rt);
}
void update(int L,int R,LL c,int l,int r,int rt){
if(l>=L&&r<=R){
lz[rt]+=c;
T[rt]+=c*(LL)(r-l+1);
return;
}
pushDown(rt,r-l+1);
int m=(l+r)>>1;
if(m>=L) update(L,R,c,l,m,rt<<1);
if(m<R) update(L,R,c,m+1,r,rt<<1|1);
pushUp(rt);
}
LL treeQuery(int L,int R,int l,int r,int rt){
if(l>=L&&r<=R)
return T[rt];
pushDown(rt,r-l+1);
int m=(l+r)>>1;
LL re=0;
if(m>=L) re+=treeQuery(L,R,l,m,rt<<1);
if(m<R) re+=treeQuery(L,R,m+1,r,rt<<1|1);
return re;
}
/*******segTree*********************************************************************/
/*******A C*****************************************************************/
int tr[MXN][26],col[MXN],fail[MXN],pos[MXN],nonum;
int dfn[MXN],cson[MXN],dfs_clock;
vector<int>g[MXN];
void ins(char *x,int ii){ //����
int le=strlen(x),p=0;
for(int i=0;i<le;i++){
int c=x[i]-'a';
if(!tr[p][c]) tr[p][c]=++nonum;
p=tr[p][c];
}
pos[ii]=p;
col[p]++; //�ַ���x����++
}
void ACGet(){
queue<int>q;
memset(fail,0,sizeof(fail));
for(int i=0;i<26;i++)if(tr[0][i]) q.push(tr[0][i]);//���ַ����,ֱ�ӽ���(0)�����Ϊ�˱���ָ���Լ�
while(!q.empty()){
int now=q.front();q.pop();
for(int i=0;i<26;i++){
if(tr[now][i]){
fail[tr[now][i]]=tr[fail[now]][i];//ָ��ǰһ��fail(��ͬ���)
q.push(tr[now][i]);
}
else tr[now][i]=tr[fail[now]][i]; //ģʽ��β����һ���ߣ�����ƥ��
}
}
}
void ACDfs(int u,int pr){
dfn[u]=++dfs_clock;
cson[u]=1;
for(auto v:g[u]){
if(v==pr)continue;
ACDfs(v,u);
cson[u]+=cson[v];
}
}
int ACQuery(char *t){
int p=0;
LL re=0;
for(int i=0;t[i];i++){
p=tr[p][t[i]-'a'];
re+=treeQuery(dfn[p],dfn[p],1,dfs_clock,1);
}
return re;
}
/*******A C******************************************************************/
char sss[MXN];
int us[MXN];
int main(){
int k,n;cin>>n>>k;
for(int i=1;i<=k;i++){
scanf("%s",sss);ins(sss,i);
}
ACGet();
for(int i=1;i<=nonum;i++){
g[fail[i]].push_back(i);g[i].push_back(fail[i]);
}
ACDfs(0,-1);
for(int i=1;i<=nonum;i++){
if(col[i]) update(dfn[i],dfn[i]+cson[i]-1,1,1,dfs_clock,1);
}
for(int i=1;i<=k;i++) us[i]=1;
while(n--){
char op;cin>>op;
if(op=='+'){
int x;scanf("%d",&x);
if(us[x]==0){
update(dfn[pos[x]],dfn[pos[x]]+cson[pos[x]]-1,1,1,dfs_clock,1);
us[x]=1;
}
}
if(op=='-'){
int x;scanf("%d",&x);
if(us[x]==1){
update(dfn[pos[x]],dfn[pos[x]]+cson[pos[x]]-1,-1,1,dfs_clock,1);
us[x]=0;
}
}
if(op=='?'){
scanf("%s",sss);
LL ans=ACQuery(sss);
cout<<ans<<'\n';
}
}
return 0;
}
树状数组+差分:
/* Author : Rshs
* Data : 2019-10-07-20.20
*/
#include<bits/stdc++.h>
using namespace std;
#define FI first
#define SE second
#define LL long long
#define MP make_pair
#define PII pair<int,int>
#define SZ(a) (int)a.size()
const double pai = acos(-1);
const double eps = 1e-10;
const LL mod = 1e9+7;
const int MXN = 1e6+5;
/*******segTree******************************************************************/
int bit[MXN];
void add(int i,LL c,int nn){ //��ai+c
while(i<=nn){
bit[i]+=c;
i=i+(i&(-i));
}
}
LL query(int i){ //ѯ��[l,r]�ĺͣ�query(r)-query(l-1)
LL re=0;
while(i>0){
re+=bit[i];
i=i-(i&(-i));
}
return re;
}
/*******segTree*********************************************************************/
/*******A C*****************************************************************/
int tr[MXN][26],col[MXN],fail[MXN],pos[MXN],nonum;
int dfn[MXN],cson[MXN],dfs_clock;
vector<int>g[MXN];
void ins(char *x,int ii){ //����
int le=strlen(x),p=0;
for(int i=0;i<le;i++){
int c=x[i]-'a';
if(!tr[p][c]) tr[p][c]=++nonum;
p=tr[p][c];
}
pos[ii]=p;
col[p]++; //�ַ���x����++
}
void ACGet(){
queue<int>q;
memset(fail,0,sizeof(fail));
for(int i=0;i<26;i++)if(tr[0][i]) q.push(tr[0][i]);//���ַ����,ֱ�ӽ���(0)�����Ϊ�˱���ָ���Լ�
while(!q.empty()){
int now=q.front();q.pop();
for(int i=0;i<26;i++){
if(tr[now][i]){
fail[tr[now][i]]=tr[fail[now]][i];//ָ��ǰһ��fail(��ͬ���)
q.push(tr[now][i]);
}
else tr[now][i]=tr[fail[now]][i]; //ģʽ��β����һ���ߣ�����ƥ��
}
}
}
void ACDfs(int u,int pr){
dfn[u]=++dfs_clock;
cson[u]=1;
for(auto v:g[u]){
if(v==pr)continue;
ACDfs(v,u);
cson[u]+=cson[v];
}
}
int ACQuery(char *t){
int p=0;
LL re=0;
for(int i=0;t[i];i++){
p=tr[p][t[i]-'a'];
re+=query(dfn[p]);
}
return re;
}
/*******A C******************************************************************/
char sss[MXN];
int us[MXN];
int main(){
int k,n;cin>>n>>k;
for(int i=1;i<=k;i++){
scanf("%s",sss);ins(sss,i);
}
ACGet();
for(int i=1;i<=nonum;i++){
g[fail[i]].push_back(i);g[i].push_back(fail[i]);
}
ACDfs(0,-1);
for(int i=1;i<=nonum;i++){
if(col[i]) add(dfn[i],1,dfs_clock),add(dfn[i]+cson[i],-1,dfs_clock);
}
for(int i=1;i<=k;i++) us[i]=1;
while(n--){
char op;cin>>op;
if(op=='+'){
int x;scanf("%d",&x);
if(us[x]==0){
add(dfn[pos[x]],1,dfs_clock),add(dfn[pos[x]]+cson[pos[x]],-1,dfs_clock);
us[x]=1;
}
}
if(op=='-'){
int x;scanf("%d",&x);
if(us[x]==1){
add(dfn[pos[x]],-1,dfs_clock),add(dfn[pos[x]]+cson[pos[x]],1,dfs_clock);
us[x]=0;
}
}
if(op=='?'){
scanf("%s",sss);
LL ans=ACQuery(sss);
cout<<ans<<'\n';
}
}
return 0;
}