HDU 4339 Query
题意: 有两个字符串,给出 Q 个询问,每个询问有两种方式:
1 p i c 把第 p 个字符串的第i 个字符换成 字符 c,
2 i 从位置i 开始,两个字符串连续相同的子串的最大长度为多少。
分析: 线段树。
数组len[rt] 保存rt 区间从最左端开始的最长子串,
合并时,如果len[rt<<1]==mid-l+1 则 len[rt]=len[rt<<1]+len[rt<<1|1] ,说明可以把右儿子区间直接接到左儿子区间上,否则由于左儿子区间与右儿子区间断开,
len[rt]=len[rt<<1].
#include<stdio.h> #include<string.h> #include<stdlib.h> const int maxn=1000005; char s1[maxn]; char s2[maxn]; int len[maxn<<3]; void creat(int l,int r,int rt) { if(l==r) { len[rt]=(s1[l]==s2[l]); return; } int m=(l+r)>>1; creat(l,m,rt<<1); creat(m+1,r,rt<<1|1); len[rt]=len[rt<<1]; if(len[rt<<1]==m-l+1) len[rt]+=len[rt<<1|1]; } void update(int l,int r,int rt,int i,int x) { if(l==r) { len[rt]=x; return; } int m=(l+r)>>1; if(i<=m) update(l,m,rt<<1,i,x); else update(m+1,r,rt<<1|1,i,x); len[rt]=len[rt<<1]; if(len[rt<<1]==m-l+1) len[rt]+=len[rt<<1|1]; } int query(int l,int r,int rt,int i) { int res=0; if(l==i) return len[rt]; int m=(l+r)>>1; if(i<=m) { res+=query(l,m,rt<<1,i); if(res==m-i+1) res+=len[rt<<1|1]; } else res+=query(m+1,r,rt<<1|1,i); return res; } int main() { int l1,l2,p,l,t,n,ca=1,k,c,flag; char s[2]; scanf("%d",&t); while(t--) { scanf("%s%s",s1+1,s2+1); l1=strlen(s1+1); l2=strlen(s2+1); l=l1<l2?l1:l2; creat(1,l,1); printf("Case %d:\n",ca++); scanf("%d",&n); while(n--) { scanf("%d",&p); if(p==1) { scanf("%d%d%s",&c,&k,s); if(k>=l) continue; flag=0; if(s1[k+1]==s2[k+1]) flag=1; if(c==1) s1[k+1]=s[0]; else s2[k+1]=s[0]; if(s1[k+1]==s2[k+1]&&flag==0) update(1,l,1,k+1,1); else if(s1[k+1]!=s2[k+1]&&flag==1) update(1,l,1,k+1,0); } else { scanf("%d",&k); if(k>=l) printf("0\n"); else printf("%d\n",query(1,l,1,k+1)); } } } return 0; }