字符串模板




kmp:
int nxt[100005];
char s[100005],t[100005];

void kmp_next(char *T,int *nt){
    nt[0]=-1;
    for(int i=0,j=-1,m=strlen(T);i<m;){
        if(j==-1||T[i]==T[j]){
        	i++,j++;
			if(T[i]!=T[j])nt[i]=j;
			else nt[i]=nt[j];
        }
        else j=nt[j];
    }
}

int kmp(char *S,char *T,int *nt){//返回T在S中出现几次
    kmp_next(T,nt);
    int ans=0,sn=strlen(S),tn=strlen(T);
    for(int i=0,j=0;i<sn;){
        if(j==-1||S[i]==T[j])i++,j++;
        else j=nt[j];
        if(j==tn)ans++;
    }
    return ans;
}



字典树:
int tree[maxn][30];
int tsize[maxn];
int tot;

void add_edge(char *s){
    int len=strlen(s);
    int rt=0;
    for(int i=0;i<len;i++){
        int xb=s[i]-'a';
        if(!tree[rt][xb])tree[rt][xb]=++tot;
        tsize[tree[rt][xb]]++;
        rt=tree[rt][xb];
    }
}

int query(char *s){//返回s是多少串的前缀
    int len=strlen(s);
    int rt=0;
    for(int i=0;i<len;i++){
        int xb=s[i]-'a';
        if(!tree[rt][xb])return 0;
        rt=tree[rt][xb];
    }
    return tsize[rt];
}



AC自动机:
struct Tire{
    static const int NODENUM=(int)1e6+5,R=26;
    int nxt[NODENUM][R],fail[NODENUM],ed[NODENUM];
    //nxt指针,fail指针,ed:在某个结点串结束的数量
    int rt,tot;
    int newnode(){
        for(int i=0;i<R;i++)nxt[tot][i]=-1;
        ed[tot]=0;
        return tot++;
    }
    void init(){
        tot=0;
        rt=newnode();
    }
    void insert(char *s){//建字典树
        int now=rt,len=strlen(s);
        for(int i=0;i<len;i++){
            int val=s[i]-'a';
            if(nxt[now][val]==-1)nxt[now][val]=newnode();
            now=nxt[now][val];
        }
        ed[now]++;
    }
    void build(){//bfs求解fail指针
        queue<int>q;
        fail[rt]=rt;
        for(int i=0;i<R;i++){
            if(nxt[rt][i]==-1)nxt[rt][i]=rt;
            else {
                fail[nxt[rt][i]]=rt;
                q.push(nxt[rt][i]);
            }
        }
        while(!q.empty()){
            int now=q.front();q.pop();
            for(int i=0;i<R;i++){
                if(nxt[now][i]==-1)nxt[now][i]=nxt[fail[now]][i];
                else {
                    fail[nxt[now][i]]=nxt[fail[now]][i];
                    q.push(nxt[now][i]);
                }
            }
        }
    }
    int query(char *s){//查询某个串所有前缀出现的次数
        int now=rt,res=0,len=strlen(s);
        for(int i=0;i<len;i++){
            int val=s[i]-'a';
            now=nxt[now][val];
            int tmp=now;
            while(tmp!=rt){
                res+=ed[tmp];
                ed[tmp]=0;
                tmp=fail[tmp];
            }
        }
        return res;
    }
}ac;



马拉车:
//abc -> *#a#b#a#\0
//下标x->2*x+2
void manacher(char *s){
	int len=strlen(s);
	for(int i=len;i>=0;--i){
		s[i+i+2]=s[i];
		s[i+i+1]='#';
	}
	s[0]='*';
	int k=1,maxlen=0;
	for(int i=2;i<len+len+1;++i){
		int maxr=k+p[k]-1;
		p[i]=min(p[2*k-i],max(maxr-i+1,1));
		while(s[i-p[i]] == s[i+p[i]])++p[i];
		if(i+p[i]>k+p[k])k=i;
		if(p[i]>maxlen)maxlen=p[i];
	}
}



回文树:
const int MAXN = 100005 ;
const int N = 26 ;

struct Palindromic_Tree {
    int next[MAXN][N] ;//next指针,next指针和字典树类似,指向的串为当前串两端加上同一个字符构成
    int fail[MAXN] ;//fail指针,失配后跳转到fail指针指向的节点
    int cnt[MAXN] ;//cnt[i]表示节点i表示的本质不同的串的个数,需跑count函数
    int num[MAXN] ;//表示以节点i表示的最长回文串的最右端点为回文串结尾的回文串个数
    int len[MAXN] ;//len[i]表示节点i表示的回文串的长度
    int S[MAXN] ;//存放添加的字符
    int last ;//指向上一个字符所在的节点,方便下一次add
    int n ;//字符数组指针
    int p ;//节点指针
	int pos[MXAN] ;//某种本质的回文串的一个右端点
    int newnode ( int l ) {//新建节点
        for ( int i = 0 ; i < N ; ++ i ) next[p][i] = 0 ;
        cnt[p] = 0 ;
        num[p] = 0 ;
        len[p] = l ;
        return p ++ ;
    }

    void init () {//初始化
        p = 0 ;
        newnode (  0 ) ;
        newnode ( -1 ) ;
        last = 0 ;
        n = 0 ;
        S[n] = -1 ;//开头放一个字符集中没有的字符,减少特判
        fail[0] = 1 ;
    }

    int get_fail ( int x ) {//和KMP一样,失配后找一个尽量最长的
        while ( S[n - len[x] - 1] != S[n] ) x = fail[x] ;
        return x ;
    }

    void add ( int c ) {
        c -= 'a' ;
        S[++ n] = c ;
        int cur = get_fail ( last ) ;//通过上一个回文串找这个回文串的匹配位置
        if ( !next[cur][c] ) {//如果这个回文串没有出现过,说明出现了一个新的本质不同的回文串
            int now = newnode ( len[cur] + 2 ) ;//新建节点
            fail[now] = next[get_fail ( fail[cur] )][c] ;//和AC自动机一样建立fail指针,以便失配后跳转
            next[cur][c] = now ;
            num[now] = num[fail[now]] + 1 ;
        }
        last = next[cur][c] ;
        cnt[last] ++ ;
        pos[last] = n;
    }

    void count () {
        for ( int i = p - 1 ; i >= 0 ; -- i ) cnt[fail[i]] += cnt[i] ;
        //父亲累加儿子的cnt,因为如果fail[v]=u,则u一定是v的子回文串!
    }
} ;



后缀数组:
#include<bits/stdc++.h>
using namespace std;

//sa[x]:排名为x的后缀的第一个字符在原串中的下标
//rk[id]:在原串中第一个字符下标为id的后缀的排名
//h[i]=height[rk[i]]:排名为i的后缀和排名为i-1的后缀的公共前缀的长度
//w~数组为桶排序用的数组
const int MAXN =(int)1e6+10;
int wa[MAXN],wb[MAXN],wv[MAXN],we[MAXN],rk[MAXN];

int cmp(int *r,int a,int b,int l){return r[a]==r[b]&&r[a+l]==r[b+l];}
void build_sa(int *r,int *sa,int n,int m){
	int i,j,p,*x=wa,*y=wb,*t;
	for(i=0;i<m;i++)we[i]=0;
	for(i=0;i<n;i++)we[x[i]=r[i]]++;
	for(i=1;i<m;i++)we[i]+=we[i-1];
	for(i=n-1;i>=0;i--)sa[--we[x[i]]]=i;
	for(j=1,p=1;p<n;j*=2,m=p){
		for(p=0,i=n-j;i<n;i++)y[p++]=i;
		for(i=0;i<n;i++)if(sa[i]>=j)y[p++]=sa[i]-j;
		for(i=0;i<n;i++)wv[i]=x[y[i]];
		for(i=0;i<m;i++)we[i]=0;
		for(i=0;i<n;i++)we[wv[i]]++;
		for(i=1;i<m;i++)we[i]+=we[i-1];
		for(i=n-1;i>=0;i--)sa[--we[wv[i]]]=y[i];
		for(t=x,x=y,y=t,p=1,x[sa[0]]=0,i=1;i<n;i++)
		x[sa[i]]=cmp(y,sa[i-1],sa[i],j)?p-1:p++;
	}
}
int height[MAXN];
void calheight(int *r,int *sa,int n){
	int i,j,k=0;
	for(i=1;i<=n;i++)rk[sa[i]]=i;
	for(i=0;i<n;height[rk[i++]]=k){
		for(k?k--:0,j=sa[rk[i]-1];r[i+k]==r[j+k];k++);
	}
}
int sa[MAXN],a[MAXN];
char str[MAXN];
int main()
{
	scanf("%s",str);
	int n=strlen(str);
	for(int i=0;i<n;i++)a[i]=str[i];
	a[n]=0;
	build_sa(a,sa,n+1,128);
	calheight(a,sa,n);
	for(int i=1;i<=n;i++)printf("%d ",sa[i]+1);
	printf("\n");
	for(int i=2;i<=n;i++)printf("%d ",height[i]);
	printf("\n");
	return 0;
}



后缀自动机:
const int M=1000100;
char s[M];
int epos[M],len[M],nxt[M][26],link[M],f[M];
int w[M],q[M];
int n,T,K;

struct sam{
    int root,last,cnt;
    sam(){root=last=++cnt;}
    void insert(int c){
        int np=++cnt,p=last;last=np;
        epos[np]=1;len[np]=len[p]+1;
        for(;p&&!nxt[p][c];nxt[p][c]=np,p=link[p]);
        if (!p) link[np]=root;
        else if (len[nxt[p][c]]==len[p]+1) link[np]=nxt[p][c];
        else{
            int nq=++cnt,q=nxt[p][c];len[nq]=len[p]+1;
            memcpy(nxt[nq],nxt[q],sizeof nxt[q]);link[nq]=link[q];
            link[np]=link[q]=nq;
            for(;p&&nxt[p][c]==q;nxt[p][c]=nq,p=link[p]);
        }
    }
    void build(){
        scanf("%s",s+1);n=strlen(s+1);
        for(int i=1;i<=n;i++)insert(s[i]-'a');
    }
    void topsort(){
        for(int i=1;i<=cnt;i++)w[len[i]]++;
        for(int i=1;i<=n;i++)w[i]+=w[i-1];
        for(int i=1;i<=cnt;i++)q[w[len[i]]--]=i;
    }
}SAM;
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值