字符串专题(标库)

1.KMP

多种颜色的方框同时框住了某个子串的右边。
st为模式串,s为匹配串。

void KMP(){
    int i,j=0;
    nx[1]=0;
    fo(i,2,n){
        while(st[j+1]!=st[i]&&j)j=nx[j];//跳不同颜色的方框
        if(st[j+1]==s[i])j++;
        p[i]=j;
    }
    j=0;
    fo(i,1,n){
        while(st[j+1]!=s[i]&&j)j=nx[j];//跳不同颜色的方框
        if(st[j+1]==s[i])j++;
        if(j==len){
             //匹配成功
        }
    }
}

2.EXKMP

像KMP一样地,模式串先自己匹配自己一次,匹配完之后再和匹配串匹配一次。
EXKMP没有跳fail链的操作。
EXKMP解决的问题是:
用相同颜色的最大的方框去框住以 s [ 1 ] s[1] s[1] s [ i ] s[i] s[i]开始的子串,且这两个子串是相同的。
e x t [ i ] ext[i] ext[i]表示 s [ 1 ] s[1] s[1] s [ i ] s[i] s[i] L C P LCP LCP
p = m a x ( i + e x t [ i ] − 1 ) p=max(i+ext[i]-1) p=max(i+ext[i]1) p = i d + e x t [ i d ] − 1 p=id+ext[id]-1 p=id+ext[id]1
现在要求 e x t [ i ] ext[i] ext[i]。考虑什么东西是匹配好了的。
考虑将 s [ i d . . . p ] s[id...p] s[id...p] s [ 1... p − i d + 1 ] s[1...p-id+1] s[1...pid+1]各用一个绿色的方框框起来,并用一个紫色的方框框住 s [ i . . . p ] s[i...p] s[i...p] s [ i − i d + 1... p − i d + 1 ] s[i-id+1...p-id+1] s[iid+1...pid+1].
这里写图片描述
现在已知2个紫色方框内的字符是一样的,所以欲知 e x t [ i ] ext[i] ext[i],就要看 e x t [ i − i d + 1 ] ext[i-id+1] ext[iid+1]了。设 l = e x t [ i − i d + 1 ] l=ext[i-id+1] l=ext[iid+1]
用红色的方框框住 s [ i − i d + 1... i − i d + l ] s[i-id+1...i-id+l] s[iid+1...iid+l] s [ 1.. l ] s[1..l] s[1..l]
那看看右边的红色方框的右端是否超过了 p p p(即: l > = p − i + 1 l>=p-i+1 l>=pi+1)。如果没有,则可以立即确定 e x t [ i ] = l ext[i]=l ext[i]=l了,否则暴力维护一个指针,暴力判。
这里写图片描述
这里写图片描述

void Exkmp(){
    int i,j,p,id,l;
    j=1;next[1]=n;
    while(j<n&&a[j]==a[j+1])j++;
    next[2]=j-1;
    id=2;
    fo(i,3,n){
        p=id+next[id]-1;l=next[i-id+1];
        if(l<p-i+1)next[i]=l;
        else{
            j=p-i+1<1?1:p-i+1;
            while(i+j-1<=n&&a[i+j-1]==a[j])j++;
            next[i]=j-1;
            id=i;
        }
    }
    j=1;
    while(j<n&&a[j]==b[j])j++;
    exB[1]=j-1;
    j=1;
    while(j<n&&a[j]==b[j+1])j++;
    exB[2]=j-2;
    id=2;
    fo(i,3,n){
        p=id+exB[id]-1;l=next[i-id+1];
        if(l<p-i+1)exB[i]=l;
        else{
            j=p-i+1<1:p-i+1;
            while(i+j-1<=n&&b[i+j-1]==a[j])j++;
            exB[i]=j-1;
            id=i;
        }
    }
}

3.Manacher

寻找最长回文子串。
同样也是个暴力,也是记录一个位置 p o s pos pos m x = m a x ( i + r [ i ] ) mx=max(i+r[i]) mx=max(i+r[i]) m x = p o s + r [ p o s ] mx=pos+r[pos] mx=pos+r[pos]
目的:求 r [ i ] r[i] r[i]
如果跨不过去就确定了 r [ i ] r[i] r[i],否则 r [ i ] + + r[i]++ r[i]++暴力判。

void Manacher(){
    int i;
    ans=0;
    scanf("%s\n",s);l=strlen(s);
    fo(i,0,l-1){st[i*2]='#';st[i*2+1]=s[i];}st[l*2]='#';
    l=l*2;r[0]=1;mx=pos=0;
    fo(i,1,l){
    	if(i<mx)r[i]=min(r[pos*2-i],mx-i);
    	   else r[i]=1;
    	while(st[i-r[i]]==st[i+r[i]]&&i-r[i]>=0&&i+r[i]<=l)r[i]++;
    	if(i+r[i]-1>mx){
    		mx=i+r[i]-1;
    		pos=i;
		}
    	ans=max(ans,r[i]-1);
	}
}

4.AC自动机

这个东西用处很多,支持计算一个字符串在另外一个串中出现的次数。
其实就是 T r i e Trie Trie上的 K M P KMP KMP,即如果 f a i l fail fail链上有当前字母的儿子,就退,并且连上去。
最后效果是怎样的?
若将从根走到 f a i l fail fail链上的所有点的路径代表的字符串拿出来,从长到短,右对齐排列好后,就会发现每一列上的字符是相同的。
不要忘记开队列!!!

void Aho_Corasick(){
    fail[0]=-1;
    Q.push(0);//Pay attention
    while(!Q.empty()){
        x=Q.front();Q.pop();
        fo(i,0,25){
            j=tr[x][i];
            if(!j)continue;
            Q.push(j);
            k=fail[x];
            while(~k&&!tr[k][i])k=fail[k];
            if(k==-1)fail[j]=0;else fail[j]=tr[k][i];
        }
    }
}

5.SA

bool comp(int *b,int x1,int x2,int len){
	int x3=x1+len>=l?-1:b[x1+len];
	int x4=x2+len>=l?-1:b[x2+len];
	return b[x1]==b[x2]&&x3==x4;
}
void SA(){
	int *A=a1,*B=b1,mx=26,len,tot;
	fo(i,0,25)cnt[i]=0;
	fo(i,0,l-1){
	    A[i]=s[i]-'a';
		cnt[A[i]]++; 
	}
	fo(i,1,25)cnt[i]+=cnt[i-1];
	fd(i,l-1,0){//倒着for循环,使得相同排名的后缀按照标号排序
		cnt[A[i]]--;
		sa[cnt[A[i]]]=i;
	}
	for(len=1;len<=l;len<<=1){
		tot=0;
		fo(i,l-len,l-1)B[tot++]=i;//后缀s[l-len]~s[l-1]的后半部分啥都没有 
		fo(i,0,l-1)
		    if(sa[i]>=len)B[tot++]=sa[i]-len;//之前排名i的后缀为sa[i],现在标号为sa[i]-len的后缀应该排到len+i
		fo(i,0,mx-1)cnt[i]=0;
		fo(i,0,l-1)cnt[A[B[i]]]++;//开始按照B[i]排序了。
		fo(i,1,mx-1)cnt[i]+=cnt[i-1];
		fd(i,l-1,0){
			cnt[A[B[i]]]--;
			sa[cnt[A[B[i]]]]=B[i];
		}
		swap(A,B);
		mx=1;
		A[sa[0]]=0;
		fo(i,1,l-1)
		if(comp(B,sa[i],sa[i-1],len))A[sa[i]]=mx-1;
		     else A[sa[i]]=mx,mx++;//产生了分歧
		if(mx==l)break;
	}
}
void HEIGHT(){
	int i,j,k=0;
	fo(i,0,l-1)rank[sa[i]]=i;
	height[0]=0;
	fo(i,0,l-1)
	    if(rank[i]){
	    	j=sa[rank[i]-1];
	    	if(k)k--;
	    	while(s[i+k]==s[j+k])k++;
	    	height[rank[i]]=k;
		}//性质:height[rank[i]]>=height[rank[i]-1]-1
}

6.SAM

int ins(int x,int wz){
    int i,np=++gs,p=wz,nq,q;
    Len[np]=Len[p]+1;sum[np]++;
    while(p&&!sam[p][x])sam[p][x]=np,p=fail[p];
    if(!p){fail[np]=1;return np;}
    q=sam[p][x];
    if(Len[q]==Len[p]+1)fail[np]=q;else{
        nq=++gs;
        fo(i,0,25)sam[nq][i]=sam[q][i];
        fail[nq]=fail[q];
        fail[np]=fail[q]=nq;
        Len[nq]=Len[p]+1;
        while(p&&sam[p][x]==q)sam[p][x]=nq,p=fail[p];
    }
    return np;
}

别忘记SAM要和数据结构扯上关系!
比如主席树维护 r i g h t right right集。

void update(int ps){
    tr[ps].sum=tr[tr[ps].ls].sum+tr[tr[ps].rs].sum;
}
void change(int &px,int py,int l,int r,int x){
    if(!px)px=++tot;
    tr[px]=tr[py];
    tr[px].sum++;
    if(l==r)return;
    int wz=(l+r)>>1;
    if(x<=wz)change(tr[px].ls,tr[py].ls,l,wz,x);
        else change(tr[px].rs,tr[py].rs,wz+1,r,x);
    update(px);
}
int merge(int px,int py){
    if(!px||!py)return px+py;
    int nx=++tot;
    tr[nx].sum=tr[px].sum+tr[py].sum;
    tr[nx].ls=merge(tr[px].ls,tr[py].ls);
    tr[nx].rs=merge(tr[px].rs,tr[py].rs);
    return nx;
}
int query(int ps,int l,int r,int x,int y){
    if(!ps)return 0;
    if(l==x&&r==y)return tr[ps].sum;
    int wz=(l+r)>>1;
    if(y<=wz)return query(tr[ps].ls,l,wz,x,y);else
    if(x>wz)return query(tr[ps].rs,wz+1,r,x,y);else
    return query(tr[ps].ls,l,wz,x,wz)+query(tr[ps].rs,wz+1,r,wz+1,y);
}
int ins(int x,int wz){
    int j,np=++gs,nq,p=wz,q;
    Len[np]=Len[p]+1;
    while(p&&!sam[p][x])sam[p][x]=np,p=fail[p];
    if(!p)fail[np]=1;else{
        q=sam[p][x];
        if(Len[q]==Len[p]+1)fail[np]=q;else{
            nq=++gs;
            fo(j,0,25)sam[nq][j]=sam[q][j];
            fail[nq]=fail[q];
            fail[np]=fail[q]=nq;
            Len[nq]=Len[p]+1;
            while(p&&sam[p][x]==q)sam[p][x]=nq,p=fail[p];
        }
    }
    change(rt[np],rt[1],1,n,i);
    P[np]=i;
    return np;
}

你可能感兴趣的:(字符串专题(标库))