BZOJ 3942 浅谈线段树维护哈希值+KMP优化暴力匹配
2017-09-22 15:29
585 查看
世界真的很大
这道题一开始就想了哈希,理论上是可以过的。。
提莫的这道题卡哈希
无可奈何写正解
看题先:
description:
有一个S串和一个T串,长度均小于1,000,000,设当前串为U串, 然后从前往后枚举S串一个字符一个字符往U串里添加,若U串后缀为T,则去掉这个后缀继续流程。
input:
The first line will contain S. The second line will contain T. The length of T will be at most that of S, and all characters of S and T will be lower-case alphabet characters (in the range a..z).
output:
The string S after all deletions are complete. It is guaranteed that S will not become empty during the deletion process.
这道题一看就先想暴力
每次匹配前面有没有后缀等于t串
发现其实我们就只是想快速知道前面n的长度是不是T串而已
而字符串这个东西不太好比较,只能一个一个的比
比较不好比较的东西,哈希
于是就想到用线段树来维护一个哈希值,每次添加,修改,查询
复杂度 nlogn
然后这道题提莫的卡哈希
当然有可能是我哈希写丑了,反正我双哈希都没过
哈希写出来了的同学可以留言。。
完整代码:
#include<stdio.h> #include<string.h> typedef unsigned long long unt; const unt base=9804799; unt p[1000010]; struct node { unt hash; int siz; node *ls,*rs; void update() { hash=ls->hash+rs->hash*p[ls->siz]; siz=ls->siz+rs->siz; } }pool[4000010],*tail=pool,*root,*root2; char ss[1000010],t[1000010],ans[1000010]; node *build(int lf,int rg) { node *nd=++tail; if(lf==rg) { nd->hash=0; nd->siz=1; nd->ls=nd->rs=0; return nd; } int mid=(lf+rg)>>1; nd->ls=build(lf,mid); nd->rs=build(mid+1,rg); nd->update(); return nd; } void modify(node *nd,int lf,int rg,int pos,char K) { if(lf==rg) { nd->hash=K-'a'+1; return ; } int mid=(lf+rg)>>1; if(pos<=mid) modify(nd->ls,lf,mid,pos,K); else modify(nd->rs,mid+1,rg,pos,K); nd->update(); } unt query(node *nd,int lf,int rg,int L,int R) { if(L<=lf&&rg<=R) return nd->hash; int mid=(lf+rg)>>1; if(R<=mid) return query(nd->ls,lf,mid,L,R); if(L>mid) return query(nd->rs,mid+1,rg,L,R); unt lt=query(nd->ls,lf,mid,L,mid); unt rt=query(nd->rs,mid+1,rg,mid+1,R); return lt+rt*p[mid-L+1]; } int main() { scanf("%s%s",ss,t);p[0]=1; int n=strlen(ss),m=strlen(t); for(int i=1;i<=n;i++) p[i]=p[i-1]*base; root=build(0,n); unt tmp=0; for(int i=0;i<m;i++) tmp+=p[i]*(t[i]-'a'+1); int p=-1; for(int i=0;i<n;i++) { ans[++p]=ss[i]; modify(root,0,n,p,ss[i]); if(p<m) continue; unt tmp2=query(root,0,n,p-m+1,p); if(tmp2==tmp) p-=m; } for(int i=0;i<=p;i++) printf("%c",ans[i]); return 0; }
好了现在来谈正解
如果是在考场上碰见这道题我肯定就直接写hash了,好写好调
但是自己刷题总还是要考虑正解的,尤其是在知道hash被卡的情况下
然后对着题目又想了一下,是不是就是KMP来处理那些没有重叠的串呢?
看样子不是的,因为样例里面很清楚的给出来了,还有可能去掉一个串后使得后面的串合并为新的模式串
之所以KMP不能处理这样的情况,是因为KMP在匹配完一个位置后不会记录这个位置匹配到哪里,而是继续匹配,换言之,无法回头
那怎么让他回头呢?记录每一个位置匹配到原串的哪一个位置就好了,就可以模拟KMP的匹配过程了,而且匹配成功之后还可以直接回到之前的位置,因为是记录了匹配位置在哪里的
完整代码:
#include<stdio.h> #include<string.h> #include<algorithm> using namespace std; int n,m,top=0,nxt[1000010],a[1000010]; char ss[1000010],t[1000010],stk[1000010]; void calnxt(char *s) { nxt[0]=-1; int i=0,j=-1; while(i<n) { if(j==-1 || s[i]==s[j]) { i++,j++; nxt[i]=j; } else j=nxt[j]; } } int main() { scanf("%s%s",ss,t); n=strlen(t),m=strlen(ss); calnxt(t); for(int i=0;i<m;i++) { stk[++top]=ss[i]; int tmp=a[top-1]; while(tmp && stk[top]!=t[tmp]) tmp=nxt[tmp]; if(stk[top]==t[tmp]) tmp++; tmp=max(tmp,0); a[top]=tmp; if(tmp==n) top-=tmp; } for(int i=1;i<=top;i++) printf("%c",stk[i]); return 0; } /* Whoso pulleth out this sword from this stone and anvil is duly born King of all England */
嗯,就是这样
相关文章推荐
- BZOJ 4034浅谈树链剖分及线段树维护
- BZOJ 2124 浅谈线段树动态维护双HASH判值域回文串
- 【BZOJ4631】踩气球【暴力】【线段树优化】
- [BZOJ2034]&[BZOJ4276]-最大收益-线段树优化建边/贪心优化最大权匹配
- bzoj1798: [Ahoi2009]Seq 维护序列seq 线段树
- BZOJ 3620 似乎在梦中见过的样子 KMP+暴力
- BZOJ_4184_shallot_线段树按时间分治维护线性基
- bzoj 1018: [SHOI2008]堵塞的交通traffic (线段树维护连通性)
- KMP 【bzoj3942】 Censoring
- CodeVS 2245 浅谈二维线段树优化间距限制型LCS动态规划状态转移
- 串的模式匹配算法(暴力/KMP)
- bzoj 维护序列seq(双标记线段树)
- bzoj 1835: [ZJOI2010]base 基站选址(线段树优化dp)
- [BZOJ] 1798: [Ahoi2009]Seq 维护序列seq #线段树:区间加+区间乘+区间求和
- [BZOJ 1018] [SHOI2008] 堵塞的交通traffic 【线段树维护联通性】
- 浅谈oracle优化中表索引维护
- Vijos P1448 校门外的树【多解,线段树,树状数组,括号序列法+暴力优化】
- BZOJ 2006 浅谈数据结构优化贪心思路
- _bzoj1798 [Ahoi2009]Seq 维护序列seq【线段树 lazy tag】
- BZOJ_1798_[AHOI2009]维护序列_线段树