自己的模板

 

目录

KMP:

EXKMP:

最大、最小表示法:

MANACHER:

AC自动机:

后缀数组:

树链剖分:

dsu  on tree:

hash

splay

二维线段树



 




KMP:

char s[maxn];///** 0~len-1
int Next[maxn];///** 1~len
///最大重复字串:if(i%tmp==0&&i/tmp>1) ans=max(ans,tmp);
///最小循环节:int res=len-Next[len];
void getnext(char* t,int Next[],int len)
{
    int j=0,k=-1;
    Next[0]=-1;
    while(j<len){
        if(k==-1||t[j]==t[k]) Next[++j]=++k;
        else k=Next[k];
    }
}

EXKMP:

char s[maxn],t[maxn];///s为主串,t为模式串
int Next[maxn],Extend[maxn];/// Extend[i]:s+i和t最长公共前缀
void EXKMP(char *s,char *t,int Next[],int Extend[])
{
    int i,j,p,L;
    int lens=strlen(s);
    int lent=strlen(t);
    Next[0]=lent;
    j=0;
    while(j+1<lent&&t[j]==t[j+1]) j++;
    Next[1]=j;
    int a=1;
    for(i=2;i<lent;i++){
        p=Next[a]+a-1;
        L=Next[i-a];
        if(i+L<p+1) Next[i]=L;
        else{
            j=max(0,p-i+1);
            while(i+j<lent&&t[i+j]==t[j]) j++;
            Next[i]=j;
            a=i;
        }
    }
    j=0;
    while(j<lens&&j<lent&&s[j]==t[j]) j++;
    Extend[0]=j;
    a=0;
    for(i=1;i<lens;i++){
        p=Extend[a]+a-1;
        L=Next[i-a];
        if(L+i<p+1) Extend[i]=L;
        else{
            j=max(0,p-i+1);
            while(i+j<lens&&j<lent&&s[i+j]==t[j]) j++;
            Extend[i]=j;
            a=i;
        }
    }
}

最大、最小表示法:

///**最小表示法,返回第一个位置。
int MINR(char *s,int len)
{
    int i=0,j=1,k=0;
    while(i<len&&j<len&&k<len){
        int t=s[(i+k)%len]-s[(j+k)%len];
        if(t==0) k++;
        else{
            if(t>0) i+=++k;
            else j+=++k;
            if(i==j) j++;
            k=0;
        }
    }
    return min(i,j);
}
///**最大表示法,返回第一个位置。
int MAXR(char *s,int len)
{
    int i=0,j=1,k=0;
    while(i<len&&j<len&&k<len){
        int t=s[(i+k)%len]-s[(j+k)%len];
        if(t==0) k++;
        else{
            if(t>0) j+=++k;
            else i+=++k;
            if(i==j) j++;
            k=0;
        }
    }
    return min(i,j);
}

MANACHER:

char s[maxn],tmp[maxn*2];///**原串,处理后的串
///**预处理原串
void init(char *s,int len)
{
    tmp[0]='$';
    cnt=0;
    for(int i=0;i<len;i++){
        tmp[++cnt]='#';
        tmp[++cnt]=s[i];
    }
    tmp[++cnt]='#';
    tmp[++cnt]='\0';
}
///**manacher,Len[i]:以i为中心的最长回文子串的半径
void manacher(char *tmp,int len,int Len[])
{
    int mx=0,ans=0,pos=0;
    for(int i=1;i<len;i++){
        Len[i]=mx>i?min(mx-i,Len[pos*2-i]):1;
        while(tmp[i-Len[i]]==tmp[i+Len[i]]) Len[i]++;
        if(Len[i]+i>mx){
            mx=Len[i]+i;
            pos=i;
        }
        ans=max(ans,Len[i]);///ans-1:最长回文字串的长度
    }
}

AC自动机:

const int maxn=1e4+10;
struct Tire
{
    int L,rt;
    int next[maxn*50][26],fail[maxn*50],end[maxn*50];
    int newnode(){
        for(int i=0;i<26;i++) next[L][i]=-1;
        end[L]=0;
        return L++;
    }
    void init(){
        L=0;
        rt=newnode();
    }
    void insert(char *s){
        int now=rt;
        for(int i=0;s[i];i++){
            int tmp=s[i]-'a';
            if(next[now][tmp]==-1){
                next[now][tmp]=newnode();
            }
            now=next[now][tmp];
        }
        end[now]++;
    }
    void build_fail(){///建fail指针
        queue<int> Q;
        fail[rt]=rt;
        Q.push(0);
        while(Q.size()){
            int u=Q.front();Q.pop();
            end[u]|=end[fail[u]];
            for(int i=0;i<26;i++){
                if(next[u][i]==-1) next[u][i]=u?next[fail[u]][i]:0;
                else{
                    fail[next[u][i]]=u?next[fail[u]][i]:0;
                    Q.push(next[u][i]);
                }
            }
        }
    }
    int query(char *s){///查询出现了几个串
        int now=rt,ans=0;
        for(int i=0;s[i];i++){
            now=next[now][s[i]-'a'];
            int tmp=now;
            while(tmp!=rt){
                if(end[tmp]){
                    ans+=end[tmp];
                    end[tmp]=0;
                }
                tmp=fail[tmp];
            }
        }
        return ans;
    }
}ac;

后缀数组:

#define LL long long

const int maxn=1e5+10;

///**后缀数组
///**可重叠最长重复字串:max(height[i])
///**不可重叠最长重复字串:二分答案,一段连续的height[i]>=k,sa(max)-sa[min)>=k -> true;
int Rank[maxn],height[maxn];///
int r[maxn];
int sa[maxn];
///**sa[i]:排序后的第i个后缀的位置  (1<=i<=len,0<=sa[i]<len)
///**Rank[i]:第i个后缀的排名  ()
///**height[i]:第i个后缀和第i-1个后缀的最长公共前缀的长度  (1<=i<=len)
char str[maxn];
int len;
int t1[maxn],t2[maxn],c[maxn];//求SA数组需要的中间变量,不需要赋值
//待排序的字符串放在s数组中,从s[0]到s[n-1],长度为n,且最大值小于m,
//除s[n-1]外的所有s[i]都大于0,r[n-1]=0
//函数结束以后结果放在sa数组中
bool cmp(int *r,int a,int b,int l)
{
    return r[a] == r[b] && r[a+l] == r[b+l];
}
void da(int str[],int sa[],int Rank[],int height[],int n,int m)
{
    n++;
    int i, j, p, *x = t1, *y = t2;
    //第一轮基数排序,如果s的最大值很大,可改为快速排序
    for(i = 0;i < m;i++)c[i] = 0;
    for(i = 0;i < n;i++)c[x[i] = str[i]]++;
    for(i = 1;i < m;i++)c[i] += c[i-1];
    for(i = n-1;i >= 0;i--)sa[--c[x[i]]] = i;
    for(j = 1;j <= n; j <<= 1)
    {
        p = 0;
        //直接利用sa数组排序第二关键字
        for(i = n-j; i < n; i++)y[p++] = i;//后面的j个数第二关键字为空的最小
        for(i = 0; i < n; i++)if(sa[i] >= j)y[p++] = sa[i] - j;
        //这样数组y保存的就是按照第二关键字排序的结果
        //基数排序第一关键字
        for(i = 0; i < m; i++)c[i] = 0;
        for(i = 0; i < n; i++)c[x[y[i]]]++;
        for(i = 1; i < m;i++)c[i] += c[i-1];
        for(i = n-1; i >= 0;i--)sa[--c[x[y[i]]]] = y[i];
        //根据sa和x数组计算新的x数组
        swap(x,y);
        p = 1; x[sa[0]] = 0;
        for(i = 1;i < n;i++)
            x[sa[i]] = cmp(y,sa[i-1],sa[i],j)?p-1:p++;
        if(p >= n)break;
        m = p;//下次基数排序的最大值
    }
    int k = 0;
    n--;
    for(i = 0;i <= n;i++)Rank[sa[i]] = i;
    for(i = 0;i < n;i++)
    {
        if(k)k--;
        j = sa[Rank[i]-1];
        while(str[i+k] == str[j+k])k++;
        height[Rank[i]] = k;
    }
}


///***求至少出现x次的字符串个数
struct que
{
    int val,pos;
}Q[maxn];

LL calc(int x)
{
    int head=1,tail=0;
    LL cnt=0;
    if(x==1){
        cnt=1ll*len*(len+1)/2;
        for(int i=1;i<=len;i++) cnt-=height[i];
        return cnt;
    }
    for(int i=1;i<x;i++){
        while(tail>=head&&height[i]<=Q[tail].val) tail--;
        Q[++tail].val=height[i];
        Q[tail].pos=i;
    }
    cnt+=Q[head].val;
    int mi=cnt;
    for(int i=x;i<=len;i++){
        while(tail>=head&&height[i]<=Q[tail].val) tail--;
        Q[++tail].val=height[i];
        Q[tail].pos=i;
        while(tail>=head&&Q[tail].pos-Q[head].pos+1>x-1) head++;
        if(Q[head].val>mi) cnt+=Q[head].val-mi;
        mi=Q[head].val;
    }
    return cnt;
}


///**** 求可重复的第K大子串

///*先二分找到第k大子串在第几个后缀中,然后再在第k个字串中二分找到第(k-check(p))个子串
LL check(int mid)/// 第1~mid个后缀一共有前几大子串
{
    LL cnt=0;
    for(int i=1;i<=mid;i++) cnt+=len-sa[i];
    int mi=maxn;
    for(int i=mid+1;i<=len;i++){
        mi=min(mi,height[i]);
        cnt+=mi;
    }
    return cnt;
}
LL check_2(int mid,int p)///***以mid位尾的子串有多少个
{
    LL cnt=mid-height[p];
    int mi=mid;
    for(int i=p+1;i<=len;i++){
        mi=min(mi,height[i]);
        if(mi<=height[p]) break;
        cnt+=mi-height[p];
    }
    return cnt;
}
void solve(LL k,int p)///确定了第K大子串的起始位置后,二分找到结束位置
{
    int l=height[p]+1,r=len-sa[p],L;
    while(l<=r){
        int mid=(l+r)>>1;
        if(check_2(mid,p)>=k) L=mid,r=mid-1;
        else l=mid+1;
    }
    for(int i=sa[p];i<sa[p]+L;i++) printf("%c",str[i]);
    printf("\n");
}

树链剖分:

///**树链剖分
int tot,num;
int head[maxn],fa[maxn],deep[maxn],son[maxn],sz[maxn],top[maxn],id[maxn];
int val[maxn];///点权
struct E{int to,next,val;}edge[maxn*2];
struct P{int u,v,val;}e[maxn];
void add_edge(int u,int v)
{
    edge[tot].to=v;
    edge[tot].next=head[u];
    head[u]=tot++;
}
void init()
{
    num=tot=0;
    memset(head,-1,sizeof(head));
}
void dfs1(int u,int f,int dep)
{
    fa[u]=f;
    deep[u]=dep;
    son[u]=0;
    sz[u]=1;
    for(int i=head[u];i!=-1;i=edge[i].next){
        int v=edge[i].to;
        if(v==f) continue;
        dfs1(v,u,dep+1);
        sz[u]+=sz[v];
        if(sz[v]>sz[son[u]]) son[u]=v;
    }
}
void dfs2(int u,int tp)
{
    id[u]=++num;
    top[u]=tp;
    if(son[u]) dfs2(son[u],tp);
    for(int i=head[u];i!=-1;i=edge[i].next){
        int v=edge[i].to;
        if(v==son[u]||v==fa[u]) continue;
        dfs2(v,v);
    }
}
int Lca(int u,int v)
{
    while(top[u]!=top[v]){
        if(deep[top[u]]<deep[top[v]]) v=fa[top[v]];
        else u=fa[top[u]];
    }
    return deep[u]<deep[v]?u:v;
}

///线段树部分
#define ls rt<<1
#define rs rt<<1|1
#define ll t[rt].l
#define rr t[rt].r
#define LL long long
struct node
{
    int l,r;
    LL val;
}t[maxn*4];
void pushup(int rt)
{
    t[rt].val=t[ls].val+t[rs].val;
}
void build(int rt,int l,int r)
{
    t[rt].l=l;
    t[rt].r=r;
    if(l==r){
        t[rt].val=(LL)val[l];
        return ;
    }
    int mid=(l+r)>>1;
    build(ls,l,mid);
    build(rs,mid+1,r);
    pushup(rt);
}
void update(int rt,int pos,int val)
{
    if(ll==rr){
        t[rt].val=(LL)val;
        return ;
    }
    int mid=(ll+rr)>>1;
    if(pos<=mid) update(ls,pos,val);
    else update(rs,pos,val);
    pushup(rt);
}
LL query(int rt,int l,int r)
{
    if(l<=ll&&rr<=r) return t[rt].val;
    int mid=(ll+rr)>>1;
    LL ans=0;
    if(l<=mid) ans+=query(ls,l,r);
    if(r>mid) ans+=query(rs,l,r);
    return ans;
}

///**边权
LL solve(int u,int v)
{
    int tp1=top[u],tp2=top[v];
    LL ans=0;
    while(tp1!=tp2){
        if(deep[tp1]<deep[tp2]) swap(u,v),swap(tp1,tp2);
        ans+=query(1,id[tp1],id[u]);
        u=fa[tp1];
        tp1=top[u];
    }
    if(u==v) return ans;
    if(deep[u]>deep[v]) swap(u,v);
    ans+=query(1,id[son[u]],id[v]);
    return ans;
}

dsu  on tree:

/*
dsu on tree
基本思想:利用树链剖分求出每个节点的重儿子。对于每棵子树,先递归求出轻儿子子树上的每个点的值,然后把轻儿子的贡献
删掉,相当于维护的东西全部清零了,然后递归求重儿子子树上的 每个点的答案,求完之后不删掉贡献,然后加上当前节点和
轻儿子子树的贡献,一颗子树就处理完了。由于每各节点最多被操作logn次,所以时间复杂度O(nlogn);

大致模板:
*/

void calc(int u,int k)
{
    cnt[val[u]]+=k;
    if(k&&cnt[val[u]]>=mx){
        if(cnt[val[u]]>mx) sum=0,mx=cnt[val[u]];
        sum+=val[u];
    }
    for(int i=head[u];i;i=e[i].next){///递归加上或减掉轻儿子的贡献
        int v=e[i].to;
        if(v==fa[u]||vis[v]) continue;
        calc(v,k);
    }
}
void dsu(int u,int sign)
{
    for(int i=head[u];i;i=e[i].next){
        int v=e[i].to;
        if(v==fa[u]||v==son[u]) continue;
        dsu(v,0);///处理轻儿子
    }
    if(son[u]) dsu(son[u],1),vis[son[u]]=1;///处理重儿子
    calc(u,1);///算当前点的答案
    ans[u]=sum;
    if(son[u]) vis[son[u]]=0;
    if(sign==0) calc(u,-1),mx=sum=0;///删掉轻儿子的贡献
}

hash

const ull X=163;
ull base[maxn],Hash[maxn],p[maxn];
void init(int len)
{
    base[0]=1;
    for(int i=1;i<=100000;i++) base[i]=base[i-1]*X;
    Hash[0]=0;
    for(int i=1;i<=len;i++) Hash[i]=Hash[i-1]*X+(s[i]-'a');
}
ull get(int l,int r) ///区间(l~r)的hash值
{
    return Hash[r]-Hash[l-1]*base[r-l+1];
}

splay

const int maxn=1e5+10;
int root,tot;
inline int read()
{
    int x=0,t=1;char ch=getchar();
    while((ch<'0'||ch>'9')&&ch!='-')ch=getchar();
    if(ch=='-')t=-1,ch=getchar();
    while(ch<='9'&&ch>='0')x=x*10+ch-48,ch=getchar();
    return x*t;
}
struct Node
{
    int ch[2];
    int ff;///父节点
    int val;///值
    int cnt;///此节点个数
    int mark;///标记
}t[maxn];
inline void pushdown(int x)
{
    if(t[x].mark)
    {
        ///...
        ///...
        t[x].mark=0;
    }
}
inline void pushup(int x)
{
    ///...
}
inline void rotate(int x)
{
    int y=t[x].ff;
    int z=t[y].ff;
    int k=t[y].ch[1]==x;
    t[z].ch[y==t[z].ch[1]]=x;t[x].ff=z;
    t[y].ch[k]=t[x].ch[k^1];t[t[x].ch[k^1]].ff=y;
    t[x].ch[k^1]=y;t[y].ff=x;
    ///pushup(y);pushup(x);
}
inline void splay(int x,int goal)///x变成goal的儿子,goal为0就是x变成根
{
    while(t[x].ff!=goal)
    {
        int y=t[x].ff;
        int z=t[y].ff;
        if(z!=goal)
            (t[y].ch[0]==x)^(t[z].ch[0]==y)?rotate(x):rotate(y);
        rotate(x);
    }
    if(goal==0)root=x;
}
inline void insert(int x)
{
    int u=root,ff=0;
    while(u&&t[u].val!=x)
    {
        ff=u;
        u=t[u].ch[x>t[u].val];
    }
    if(u) t[u].cnt++;
    else
    {
        u=++tot;
        if(ff)t[ff].ch[x>t[ff].val]=u;
        t[u].ch[0]=t[u].ch[1]=0;
        t[u].val=x;t[u].ff=ff;
        t[u].cnt=1;
    }
    splay(u,0);
}
inline void find(int x)
{
    int u=root;
    if(!u)return;
    while(t[u].ch[x>t[u].val]&&x!=t[u].val)
        u=t[u].ch[x>t[u].val];
    splay(u,0);
}
inline int Next(int x,int f) ///f=1->前驱;f=0->后继
{
    find(x);
    int u=root;
    if(t[u].val>=x&&f)return u;
    if(t[u].val<=x&&!f)return u;
    u=t[u].ch[f];
    while(t[u].ch[f^1])u=t[u].ch[f^1];
    return u;
}
inline void Delete(int x)//删除x
{
    int last=Next(x,0);//查找x的前驱
    int next=Next(x,1);//查找x的后继
    splay(last,0);splay(next,last);
    //将前驱旋转到根节点,后继旋转到根节点下面
    //很明显,此时后继是前驱的右儿子,x是后继的左儿子,并且x是叶子节点
    int del=t[next].ch[0];//后继的左儿子
    if(t[del].cnt>1)//如果超过一个
    {
        t[del].cnt--;//直接减少一个
        splay(del,0);//旋转
    }
    else
        t[next].ch[0]=0;//这个节点直接丢掉(不存在了)
}

二维线段树

///维护矩形内最大最小值,单点修改
const int maxn=800+10;
const int inf=1e9+10;
int val[maxn][maxn];
int mx[maxn<<2][maxn<<2],mi[maxn<<2][maxn<<2];///维护矩阵内最大最小值
int MX,MI;///最大最小值
int x,y,L;
int n;
int lx,rx,ly,ry;///矩形范围
void pushup(int rtx,int rt)
{
    mx[rtx][rt]=max(mx[rtx][rt<<1],mx[rtx][rt<<1|1]);
    mi[rtx][rt]=min(mi[rtx][rt<<1],mi[rtx][rt<<1|1]);
}
void buildy(int rtx,int x,int rt,int l,int r)
{
    if(l==r){
        if(x==-1){
            mx[rtx][rt]=max(mx[rtx<<1][rt],mx[rtx<<1|1][rt]);
            mi[rtx][rt]=min(mi[rtx<<1][rt],mi[rtx<<1|1][rt]);
        }
        else mx[rtx][rt]=mi[rtx][rt]=val[x][l];
        return ;
    }
    int mid=(l+r)>>1;
    buildy(rtx,x,rt<<1,l,mid);
    buildy(rtx,x,rt<<1|1,mid+1,r);
    pushup(rtx,rt);
}
void buildx(int rt,int l,int r)
{
    if(l==r){
        buildy(rt,l,1,1,n);
        return ;
    }
    int mid=(l+r)>>1;
    buildx(rt<<1,l,mid);
    buildx(rt<<1|1,mid+1,r);
    buildy(rt,-1,1,1,n);
}
void query_y(int rtx,int rt,int l,int r)
{
    if(ly<=l&&r<=ry){
        MI=min(MI,mi[rtx][rt]);
        MX=max(MX,mx[rtx][rt]);
        return ;
    }
    int mid=(l+r)>>1;
    if(ly<=mid) query_y(rtx,rt<<1,l,mid);
    if(ry>mid) query_y(rtx,rt<<1|1,mid+1,r);
}
void query_x(int rt,int l,int r)
{
    if(lx<=l&&r<=rx){
        query_y(rt,1,1,n);
        return ;
    }
    int mid=(l+r)>>1;
    if(lx<=mid) query_x(rt<<1,l,mid);
    if(rx>mid) query_x(rt<<1|1,mid+1,r);
}
void updatey(int rtx,int x,int rt,int l,int r,int val)
{
    if(l==r){
        if(x==-1){
            mx[rtx][rt]=max(mx[rtx<<1][rt],mx[rtx<<1|1][rt]);
            mi[rtx][rt]=min(mi[rtx<<1][rt],mi[rtx<<1|1][rt]);
        }
        else mx[rtx][rt]=mi[rtx][rt]=val;
        return ;
    }
    int mid=(l+r)>>1;
    if(y<=mid) updatey(rtx,x,rt<<1,l,mid,val);
    else updatey(rtx,x,rt<<1|1,mid+1,r,val);
    pushup(rtx,rt);
}
void updatex(int rt,int l,int r,int val)///把(x,y)点的值改为val
{
    if(l==r){
        updatey(rt,l,1,1,n,val);
        return ;
    }
    int mid=(l+r)>>1;
    if(x<=mid) updatex(rt<<1,l,mid,val);
    else updatex(rt<<1|1,mid+1,r,val);
    updatey(rt,-1,1,1,n,val);
}

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值