...日后会慢慢补(flag!)先来讲讲基本的
一.字符串Hash
将字符串用一个数表示,常用的写法有:
1.自然溢出
2.单Hash
3.双Hash
前两个会被精心构造的串卡掉,最后一个虽然目前卡不掉,但是出题人可以卡你常数。
所以这个算法很Naive?不是的
我们来看一道题 bzoj1014
用splay维护字符串的最长公共前缀,那么问题来了,没有一种OI比赛中常用的数据结构能维护一个动态的字符串
这时字符串哈希就派上了用场
我们用一个哈希值来表示字符串
在splay上跑一个二分查找就好了
具体的Hash方法网上都有
这里仅介绍思想
#include<iostream> #include<cstdio> #include<cstdlib> #include<cmath> #include<cstring> #include<algorithm> #include<vector> #include<queue> #include<set> #include<map> #include<stack> #define ll unsigned long long #define pi 3.14 #define eps 1e-9 #define inf 2147483233 #define m(a) memset(a,0,sizeof(a)) #define M(a) memset(a,127,sizeof(a)) #define REP(i,m,n) for(int i=1;i<=n;i++) #define DWN(i,n,m) for(int i=n;i>=1;i++) #define lowbit(x) x&(-x) #define POJ(n) while(~scanf("%d",&n) && n) using namespace std; int n; inline int read() { int x=0,f=1; char ch=getchar(); while(!isdigit(ch)){if(ch==‘-‘)f=-1;ch=getchar();} while(isdigit(ch)){x=10*x+ch-‘0‘;ch=getchar();} return x*f; } inline void write(int x) { int num=0; char buf[15]; while(x)buf[++num]=(x%10)+‘0‘,x/=10; while(num)putchar(buf[num--]); putchar(‘\n‘); } ll powe[150000]; void makepow() { int i; for(powe[0]=1,i=1;i<140142;i++)powe[i]=powe[i-1]*131; } const int maxn=140142; char str[maxn]; struct SplayTree { int rt,Size; int son[maxn][2],f[maxn],size[maxn]; ll val[maxn],hsh[maxn]; char s[maxn]; inline void pushup(int x){size[x]=size[son[x][0]]+size[son[x][1]]+1;hsh[x]=hsh[son[x][0]]+powe[size[son[x][0]]]*s[x]+powe[size[son[x][0]]+1]*hsh[son[x][1]];} inline void Rotate(int x,int type) { int y=f[x]; son[y][!type]=son[x][type]; f[son[x][type]]=y; f[x]=f[y]; if(f[x])son[f[y]][son[f[y]][1]==y]=x; son[x][type]=y; f[y]=x; size[x]=size[y],hsh[x]=hsh[y]; pushup(y); } inline void splay(int x,int goal) { while(f[x]!=goal) { if(f[f[x]]==goal) { if(son[f[x]][0]==x)Rotate(x,1); else Rotate(x,0); } else { int y=f[x],z=f[y]; if(son[z][0]==y) { if(son[y][0]==x)Rotate(y,1),Rotate(x,1); else Rotate(x,0),Rotate(x,1); } else { if(son[y][1]==x)Rotate(y,0),Rotate(x,0); else Rotate(x,1),Rotate(x,0); } } } if(goal==0) rt=x; } inline int rank(int x,int k) { if(k<=size[son[x][0]]) return rank(son[x][0],k); else if(k==size[son[x][0]]+1) return x; else return rank(son[x][1],k-size[son[x][0]]-1); } inline int build(int l,int r,int id) { if(l>r)return 0; int x=++Size,mid=(l+r)>>1; f[x]=id; s[x]=str[mid]; son[x][0]=build(l,mid-1,x); son[x][1]=build(mid+1,r,x); pushup(x); return x; } inline void insert(int k,char val) { int x=rank(rt,k),y=rank(rt,k+1); splay(x,0),splay(y,x); s[++Size]=val; f[Size]=y,son[y][0]=Size; pushup(Size); pushup(y); pushup(x); } inline void change(int k,int val) { int x=rank(rt,k); splay(x,0); s[x]=val; pushup(x); } inline int bisearch(int kx,int ky) { int l=0,r=n,mid; while(l<=r) { mid=l+r>>1; if(ky+mid>n+2) { r=mid-1; continue; } int x=rank(rt,kx-1),y=rank(rt,kx+mid); splay(x,0),splay(y,x); ll temp=hsh[son[y][0]]; x=rank(rt,ky-1),y=rank(rt,ky+mid); splay(x,0),splay(y,x); if(temp==hsh[son[y][0]])l=mid+1; else r=mid-1; } return r; } }splay; int main() { int m,i,j,k,x,y; char op[10],val[10]; scanf("%s%d",str+1,&m); makepow(); n=strlen(str+1); splay.rt=splay.build(0,n+1,0); for(i=1;i<=m;i++) { scanf("%s",op); if(op[0]==‘I‘) { scanf("%d%s",&x,val); splay.insert(x+1,val[0]); n++; } else if(op[0]==‘R‘) { scanf("%d%s",&x,val); splay.change(x+1,val[0]); } else { scanf("%d%d",&x,&y); if(x>y) swap(x,y); if(x!=y) printf("%d\n",splay.bisearch(x+1,y+1)); else printf("%d\n",n-x+1); } } return 0; }
#include <bits/stdc++.h> using namespace std; typedef long long LL; typedef pair <int, int> pii; const int N = 2e6 + 5; const int seed = 131; int mod[2] = {1000000007, 1000000009}; char S[N]; //输入字符串 int F[2][N]; //seed ^ a int pre[2][N]; int n, g, w; int tim, vis[N], ans[N]; map <pii, int> cnt; void pre_init(){ //deal with seed ^ n for(int t = 0; t <= 1; t++){ F[t][0] = 1; for(int i = 1; i < N; i++){ F[t][i] = (LL)F[t][i-1] * seed % mod[t]; } } } pii Hash(string s){ //Hash a string int ret[2] = {0}; int len = s.size(); for(int t = 0; t <= 1; t++){ for(int i = 0; i < len; i++){ ret[t] = ((LL) ret[t] * seed + s[i]) % mod[t]; } } return pii(ret[0], ret[1]); } pii Hash(int l, int r) { int ret[2]; for(int t = 0; t <= 1; t++){ ret[t] = (pre[t][r] - (LL)pre[t][l-1] * F[t][r-l+1] % mod[t] + mod[t]) % mod[t]; } return pii(ret[0], ret[1]); } void pre_solve(){ int len = strlen(S + 1); for(int t = 0; t <= 1; t++){ pre[t][0] = 0; for(int i = 1; i <= len; i++){ pre[t][i] = ((LL)pre[t][i-1] * seed + S[i]) % mod[t]; } for(int j = len + 1, i = 1; i <= w - 1; i++, j++){ pre[t][j] = ((LL)pre[t][j-1] * seed + S[i]) % mod[t]; } } } bool check(int id){ tim++; int l = id, r = id + w - 1; for(int i = 1; i <= n; i++, l += w, r += w){ pii t = Hash(l, r); if(!cnt.count(t)) return 0; //目标串中没有这个子串 int p = cnt[t]; if(vis[p] == tim) return 0; //目标串出现了大于1次 vis[p] = tim; ans[i] = p; } printf("YES\n"); for(int i = 1; i <= n; i++) cout << ans[i] <<" "; cout << endl; return 1; } int main() { pre_init(); scanf("%d%d%s", &n, &w, S+1); pre_solve(); scanf("%d", &g); for(int i = 1; i <= g; i++){ scanf("%s", S); pii t = Hash(S); cnt[t] = i; } bool mark = 0; for(int i = 1; i <= w; i++){ if(check(i)){ mark = 1; break; } } if(mark == 0){ printf("NO\n"); } return 0; }
模数之一要用19260817这个大质数
有奇效
二.K(kan)M(mao)P(pian)
一类用于单字符串匹配的O(n+m)的算法
解释也不是很好讲
直接上模板吧
#include<cstdio> #include<string> #include<iostream> using namespace std; int p[101]; int main() { string a,b; cin>>a>>b; int n=a.length(),m=b.length(); a=" "+a;b=" "+b; int j=0; for(int i=2;i<=m;i++) { while(j>0&&b[j+1]!=b[i])j=p[j]; if(b[j+1]==b[i])j++; p[i]=j; } j=0; for(int i=1;i<=n;i++) { while(j>0&&b[j+1]!=a[i])j=p[j]; if(b[j+1]==a[i])j++; if(j==m){printf("%d",i-m+1);break;} } return 0; }
三.AC自动机
/*多么希望AC和自动能够换一下*/
就是在Trie树上跑KMP
每个点都有一个Fail指针表示如果这个点匹配不上跳到哪个地方
四.字符串dp
1.O(n^2)可以过的dp
设计状态一般至少为dp[i][j]表示第一个串的前i个和第二个串的前j个怎么这么样
2.AC自动机上的dp
两种做法:第一个是构建Fail树,跑树形dp
另外一种不能在Fail树上做的,一般有一维表示"当前在AC自动机的x号节点上"