标签:前缀 插入 char 基本操作 二分 origin type for 问题
它的主要目的是判重,用于DFS、BFS判重(八数码),字符串判断相等、出现等等。
本篇总结字符串哈希以及一些应用例题。
因为取出一个字符串是O(n)的,比较一遍又是O(n)的,况且要比较两个甚至多个。这就成了n^2级别的了。
那我们比较数字怎么就不用这么麻烦呢?因为数字可以直接比较,(虽然不知道内部是怎么实现的,反正比一位一位比较肯定快)所以我们考虑把字符串映射到数字上。
就有了字符串哈希。
通过字符串哈希,只要题目支持预处理,我们可以O(n)预处理之后,O(1)进行提取,O (1)进行判重。
1.字符。初始坐标无所谓。
2.K进制数,通常选择131,13331,这两个质数冲突几率很小(不要问我为什么)。
3.取模数,我用过 1e9+7,998244353,用2^64也可以,这里利用自然溢出,一般不会有问题。提一句,unsigned long long做减法,即使算出来应该是负数,会自动加上2^64,相当于(a+mod-b)%mod了。没有问题。
1.预处理K^len 放入k[]中储存。
2.顺便处理hash[i]=hash[i-1]*K+str[i]
1.提取:a[l,r]段:hash[r]-hash[l-1]*k[r-l] 类似前缀和。
2.插入,同处理。
操作均是O(1)
1.判断字符串是否相等。取hash段比较即可,O(1)
2.找某两个位置开始的LCP(最长公共前缀),二分位置+hash判断 O(logn) (长度够小,可用trie树,更好的支持多串LCP)
3.判断两个串字典序大小,找LCP,判断下一位大小。O(logn)
T1:POJ2758
给定一个字符串,要求维护两种操作
在字符串中插入一个字符
询问某两个位置开始的LCP
插入操作<=200,字符串长度<=5w,查询操作<=2w
分析:有人用后缀数组??不会。Splay??不会。
操作小于等于200,直接暴力重构是正解!!
注意:
1.插入字符位置可能远大于len,要向len+1取min
2.询问位置是初始位置,重构的时候,可以暴力循环记录每一个初始位置现在已经变到了第几个位置。
#include<cstdio> #include<cstdlib> #include<algorithm> #include<iostream> #include<cmath> #include<cstring> using namespace std; typedef long long ll; const int N=80000+210; const int mod=998244353; const int K=13331; ll h[N]; ll c[N]; int n,m; int len; int f[N]; int ne[N]; char o[N],a[N]; int main() { scanf("%s",o+1); n=strlen(o+1); memcpy(a+1,o+1,sizeof o);len=n; //cout<<" lenn "<<len<<endl; scanf("%d",&m); for(int i=1;i<=n;i++) ne[i]=i; c[0]=1; for(int i=1;i<=n+m+1;i++) { c[i]=(c[i-1]*K)%mod; if(i<=n) h[i]=(h[i-1]*c[1]+(int)o[i])%mod; } char ch,op; int num,x,y; //cout<<"fir "<<a+1<<endl; while(m--){ scanf(" %c",&op); if(op==‘Q‘){ scanf("%d%d",&x,&y); x=ne[x],y=ne[y]; //cout<<x<<" and "<<y<<endl; if(a[x]!=a[y]){ printf("0\n");continue; } int ans; int l=0,r=min(len-x,len-y)+1; //cout<<" origin "<<l<<" "<<r<<endl; while(l<=r){ int mid=(l+r)>>1; int ed1=x+mid-1; int ed2=y+mid-1; ll ha1=(h[ed1]+mod-h[x-1]*c[mid]%mod)%mod; ll ha2=(h[ed2]+mod-h[y-1]*c[mid]%mod)%mod; //cout<<mid<<" hash "<<ha1<<" "<<ha2<<endl; if(ha1==ha2) { ans=mid,l=mid+1; } else{ r=mid-1; } } printf("%d\n",ans); } else{ scanf(" %c%d",&ch,&num); if(num>len) num=len+1; ///add(num); len++; for(int i=len;i>=num+1;i--) a[i]=a[i-1]; a[num]=ch; for(int i=num;i<=len;i++) h[i]=(h[i-1]*c[1]+(int)a[i])%mod; for(int i=n;i>=1;i--) { if(ne[i]>=num) ne[i]++;else break;} } //cout<<a+1<<endl; } return 0; }
标签:前缀 插入 char 基本操作 二分 origin type for 问题
原文地址:https://www.cnblogs.com/Miracevin/p/9153309.html