您的位置:首页 > 理论基础 > 计算机网络

[计蒜客16956] Query on a string [2017 ACM-ICPC 亚洲区(乌鲁木齐赛区)网络赛 G]

2017-09-21 18:15 603 查看

题意

给定字符串S和T

两种操作:

1. 修改S串某个位置的字符

2. 询问S某子串中出现了多少次T串

|T|≤10

题解

如果没有修改,我们可以对T建fail指针,然后在S中kmp一遍并记录哪些位置匹配了完整的T串,这相当于一个01数组,询问则是对这个01数组求区间和,可以简单地用树状数组实现。这样一次询问就是O(logn)的。

当修改某个字符时,01数组中受到影响的显然最多是10个位置,即每次修改操作暴力kmp并修改对应位置的01数组即可。这样一次修改就是O(logn)的,常数为10左右。于是整体复杂度O(qlogn)。

代码

#include <bits/stdc++.h>

#define  kN  100010LL
#define  kL  100010LL
#define  lb  (p&(-p))

int q,n,m,fail[20],C[kL];
bool ok[kL];
char s[kL],t[20];

inline void ins(int p,int w){
for(;p<=n;p+=lb)C[p]+=w;
}
inline int sum(int p) {
int ret=0;for(;p>0;p-=lb)ret+=C[p];return ret;
}

inline void getfail() {
int i, p = 0;
fail[1] = 0;
for(i=2;i<=m;i++) {
while(p&&t[p+1]!=t[i])p=fail[p];
p+=(t[p+1]==t[i]), fail[i] = p;
}
}

inline void Query(int l,int r) {
l += m-1;
if (l<=r) printf("%d\n",sum(r)-sum(l-1));
else puts("0");
}

inline void Change(int p,char c) {
if (c == s[p]) return;
s[p] = c;
int l = p-m+1, r = p+m-1, i;
if (l<1) l = 1;
if (r > n) r = n;
int pos = p;
p = 0;
for(i = l;i <= r;i ++ ) {
while(p && t[p+1]!=s[i]) p = fail[p];
p += (t[p+1]==s[i]);
if (i >= pos) {
if (p == m) {
if (!ok[i]) ok[i]=true,ins(i,1);
} else {
if (ok[i]) ok[i] = false, ins(i,-1);
}
}
}
}

inline void work() {
int p, i, j, k;
char mode, c;
scanf("%d%s%s",&q,s+1,t+1);
n=strlen(s+1);
m=strlen(t+1);
getfail();
memset(ok,0,sizeof ok);
memset(C,0,sizeof C);
p = 0;
for(i=1;i<=n;i++) {
while(p && t[p+1]!=s[i]) p = fail[p];
p += (t[p+1]==s[i]);
if (p == m) ok[i]=true,ins(i,1);
}
while(q-->0) {
while(mode=getchar(),mode!='Q'&&mode!='C');
if (mode == 'Q') {
scanf("%d%d",&j,&k);
Query(j,k);
} else {
scanf("%d",&j);
while(c=getchar(),c<'!');
Change(j,c);
}
}
puts("");
}

int main() {
int T;
scanf("%d",&T);
while(T-->0) work();
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: 
相关文章推荐