板子合集,持续更新中...

树上倍增求LCA

const int N = 1e5+5;
int lca[N][20];
int dep[N];
void dfs(int now,int f,int depth){///初始时dfs(根节点,0,1);
    lca[now][0] = f;
    dep[now] = depth;
    for (int i=head[now];i!=0;i=edge[i].last){//链式前向星
        int v = edge[i].to;
        if (v==f) continue; 
        dfs(v,now,depth+1);
    }
}

void getst(int n){
    for (int j=1;(1<<j)<=n;j++)
        for (int i=1;i<=n;i++)//这里千万别写成i+(1<<j)-1<=n,因为这不是维护区间
            lca[i][j] = lca[lca[i][j-1]][j-1];
}

int getlca(int x,int y){
    if (dep[x]<dep[y]) swap(x,y);
    int differ = dep[x]-dep[y];
    for (int i=0;(1<<i)<=differ;i++){
        if ((1<<i)&differ){
            x = lca[x][i];
        }
    }
    if (x==y) return x;//忘了这行的话答案去下面传父亲就不对了
    for (int i=18;i>=0;i--){
        if (lca[x][i]!=lca[y][i]) x = lca[x][i],y = lca[y][i];
    }
    return lca[x][0];
}

最小表示法

///求所有循环同构中字典序最小或者最大
int getmin(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) k++;
        else {
            if (t<0) j += k+1;
            else i += k+1;
            if (i==j) j++;
            k = 0;
        }
    }
    return i<j?i:j;
}
 
int getmax(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) k++;
        else {
            if (t>0) j += k+1;
            else i += k+1;
            if (i==j) j++;
            k = 0;
        }
    }
    return i<j?i:j;
}

Manacher

精简版

void manacher(char* s){
    //init
    ss[0] = '@';
    int len = strlen(s);
    for1(i,1,len){
        ss[2*i-1] = '#';
        ss[2*i] = s[i-1];
    }
    ss[2*len+1]='#';
    ss[2*len+2]='$';
    ss[2*len+3] = '\0';
    len = len*2+4;

    int ans = 0;

    int pos=1,maxp=1,j;
    pal[0] = pal[1] = 1;
    for (int i=2;i<len;i++){
        if (pal[2*pos-i]<maxp-i+1) pal[i] = pal[2*pos-i];
        else {
            j = maxp-i+1;
            //if (j<=1) j = 1;
            while (i-j>=0 && i+j<len && ss[i+j]==ss[i-j]) j++;
            pal[i] = j,pos = i,maxp = max(maxp,i+pal[i]-1);
            ans = max(ans,pal[i]-1);
        }
    }
    printf("%d\n",ans);
}

 复杂版

const int maxn=1000010;
char str[maxn];//原字符串
char tmp[maxn<<1];//转换后的字符串
int Len[maxn<<1];
//转换原始串
int INIT(char *st)
{
    int i,len=strlen(st);
    tmp[0]='@';///开头加一个不等于#也不等于字符串的字符,这样就不用判断左边越界了,那么右边万一比到n+1怎么办呢?有\0吗?不,\0在n处,解决办法看16行
    for(i=1;i<=2*len;i+=2)
    {
        tmp[i]='#';
        tmp[i+1]=st[i/2];
    }
    tmp[2*len+1]='#';
    tmp[2*len+2]='$';///结尾搞一个不是@也不是#的字符
    tmp[2*len+3]=0;
    return 2*len+1;//返回转换字符串的长度
}
//Manacher算法计算过程
int MANACHER(char *st,int len)
{
     int mx=0,ans=0,po=0;//mx即为当前计算回文串最右边字符的最大值
     for(int i=1;i<=len;i++)
     {
         if(mx>i)
         Len[i]=min(mx-i,Len[2*po-i]);//在Len[j]和mx-i中取个小
         else
         Len[i]=1;//如果i>=mx,要从头开始匹配
         while(st[i-Len[i]]==st[i+Len[i]])
         Len[i]++;
         if(Len[i]+i>mx)//若新计算的回文串右端点位置大于mx,要更新po和mx的值
         {
             mx=Len[i]+i;
             po=i;
         }
         ans=max(ans,Len[i]);
     }
     return ans-1;//返回Len[i]中的最大值-1即为原串的最长回文子串额长度
}

Len[st+ed]-1>=ed-st+1判断区间是否回文


EXKMP

精简版

void exkmp(char *a,char *b){
    //getnt
    int pos=0,maxp=0,j;
    int lena = strlen(a),lenb = strlen(b);
    nxt[0] = lenb; 
    for (int i=1;i<lenb;i++){
        if (nxt[i-pos]<maxp-i+1) nxt[i] = nxt[i-pos];
        else {
            j = maxp-i+1;//if (j<0) j = 0;
            while (i+j<lenb && b[i+j]==b[j]) j++;
            nxt[i] = j,pos = i,maxp = max(maxp,max(i+nxt[i]-1,i));
        }
    }
    //getex
    pos = -1,maxp = -1;
    for (int i=0;i<lena;i++){
        if (nxt[i-pos]<maxp-i+1) ex[i] = nxt[i-pos];
        else {
            j = maxp-i+1;//if (j<0) j = 0;
            while (i+j<lena && j<lenb && a[i+j]==b[j]) j++;
            ex[i] = j,pos = i,maxp = max(maxp,max(i+ex[i]-1,i));
        }
    }
}

 不精简版

const int maxn=100010;   //字符串长度最大值
int nt[maxn],ex[maxn]; //ex数组即为extend数组
///预处理计算next数组
void GETNEXT(char *str)
{
    int i=0,j,po,len=strlen(str);
    nt[0]=len;///用自己作为后缀与自己匹配
    while(i+1<len && str[i]==str[i+1]) i++;///暴力求next[1]
    nt[1]=i;
 
    po=1;///从此点出发next数组延伸位置最远
    for(i=2;i<len;i++)
    {
        if(nt[i-po]< nt[po]+po-i )///第一种情况,可以直接得到next[i]的值
        nt[i]=nt[i-po];
        else///第二种情况,要继续匹配才能得到next[i]的值
        {
            j=nt[po]+po-i;
            if(j<0)j=0; ///小于0表示没有已知相同部分,重新开始匹配
            while(i+j<len&&str[j]==str[j+i])
            j++;
            nt[i]=j;
            po=i;///更新po的位置
        }
    }
}
///计算extend数组
void EXKMP(char *s1,char *s2)
{
    int i=0,j,po,len=strlen(s1),l2=strlen(s2);
    GETNEXT(s2);
    while(i<l2&&i<len&&s1[i]==s2[i])
    i++;
    ex[0]=i;
    po=0;
    for(i=1;i<len;i++)
    {
        if(nt[i-po]<ex[po]+po-i)
        ex[i]=nt[i-po];
        else
        {
            j=ex[po]+po-i;
            if(j<0)j=0;
            while(i+j<len&&j<l2&&s1[j+i]==s2[j])
            j++;
            ex[i]=j;
            po=i;
        }
    }
}

KMP

#include<bits/stdc++.h>
const int N = 1e6+5;
using namespace std;
int nt[N];
char s[N];
char ss[N];
int KMP() ///求大串中有几个子串
{
    int len=strlen(s);
    int t=strlen(ss);
    nt[0]=-1;
    int cnt=0;
    ///构造next数组
    for (int i=0,j=-1; i<t; ){
        if (j==-1 || ss[i]==ss[j]){
            i++;
            j++;
            nt[i]=j;
        }
        else j=nt[j];
    }
    for (int i=0;i<t;i++) printf("nt[%d]=%d ",i,nt[i]); printf("\n");
    ///开始遍历大串找其中的小串
    for (int i=0,j=0; i<len; ){
        if (j==-1 || ss[j]==s[i]){
            i++;
            j++;
        }
        else j=nt[j];
        if (j==t){
            cnt++;
            j=0;
        }
    }
    return cnt;
}
 
int main()
{
    while (cin>>s){
        cin>>ss;
        cout<<KMP()<<endl;
    }
}

字典树---普通版本

int trie[maxnode][sigma_size];///maxnode表示节点数,maxnode=单词数*每个单词最大长度(最坏情况每个单词组合都不一样)
                              ///sigma_size 表示一个节点最多延伸出多少各节点,也就是字符种类
///  trie[i][j] 的值表示 i节点指向j这个数字对应字母 的节点的位置
bool val[maxnode];            ///为真表示当前节点是一个单词的结束
int sz;
 
int idx(char ch) { return ch-'a';}//将字母转换成对应数字,如果还有大写,标点的话要更麻烦些
 
void init(int x){
    val[x] = false;
    memset(trie[x],0,sizeof trie[x]);
}
 
void insert(char *s){///插入
    int u = 0;
    for (int i=0;s[i];i++){
        int v = idx(s[i]);
        if (!trie[u][v]){
            init(sz);///初始化多组输入时上一次残留的数据
            trie[u][v] = sz++;
        }
        u = trie[u][v];
    }
    val[u] = true;
}
 
bool query(char *s){
    int u = 0;
    for (int i=0;s[i];i++){
        int v = idx(s[i]);
        if (!trie[u][v]) return false; ///这个节点还没有开辟
        u = trie[u][v];
    }
    return true;
}

领接表存字典树

#include<cstdio>
#include<cstring>
#define ll long long
 
using namespace std;
 
const int N = 4000*1001 + 5;
 
struct node
{
    int son;
    int right;
    int sum;
    char ch;
}trie[N];
int id;
ll ans;
 
void init()
{
    ans = 0;
    id = 1;
    trie[0].right = 0;
    trie[0].son = 0;
    trie[0].sum = 0;
}
 
void insert(char *s)
{
    int u = 0,j;
    int len = strlen(s);
    for (int i=0;i<=len;i++){
        bool flag = false;
 
        for (j=trie[u].son;j!=0;j=trie[j].right){
            if (s[i]==trie[j].ch){
                flag = true;
                break;
            }
        }
 
        if (!flag){
            j = id++;
            trie[j].right = trie[u].son;
            trie[u].son = j;
            trie[j].ch = s[i];
 
            trie[j].son = 0;
            trie[j].sum = 0;
        }
 
        ans += (trie[u].sum+trie[j].sum);
        if (i==len) trie[j].sum++;
 
        trie[u].sum++;
        u = j;
    }
}
 
int main()
{
    int n;
    char in[1010];
    for (int kca=1;scanf("%d",&n),n;kca++){
        init();
        while (n--) scanf("%s",in),insert(in);
        printf("Case %d: %lld\n",kca,ans);
    }
}

后缀数组求两个串LCS----最长公共子串

#include<cstdio>
#include<algorithm>
#include<cstring>
#define debug(x) printf("----Line%s----\n",#x)
 
using namespace std;
 
const int N = 1e5+5;
 
int wa[N<<1],wb[N<<1],sa[N<<1],rnk[N<<1],cnt[N<<1],height[N<<1];
char s[N<<1];
 
void build(int n,int m)
{
    int *x=wa,*y=wb,p,i,j;
    for (i=0;i<m;i++) cnt[i] = 0;       
    for (i=0;i<n;i++) cnt[x[i]=s[i]]++;  
    for (i=1;i<m;i++) cnt[i] += cnt[i-1];
    for (i=n-1;i>=0;i--) sa[--cnt[x[i]]] = i;
 
    for (j=1,p=1;p<n;j<<=1,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<m;i++) cnt[i] = 0;
        for (i=0;i<n;i++) cnt[x[y[i]]]++;
        for (i=1;i<m;i++) cnt[i] += cnt[i-1];
        for (i=n-1;i>=0;i--) sa[--cnt[x[y[i]]]] = y[i];
 
        swap(x,y); 
        x[sa[0]] = 0;
        p = 1;
        for (i=1;i<n;i++)
            x[sa[i]] = ( y[sa[i]]==y[sa[i-1]] && y[sa[i]+j]==y[sa[i-1]+j] )? p-1:p++;
    }
}
 
void getheight(int n)
{
    int k=0;
    for (int i=0;i<n;i++) rnk[sa[i]] = i;
    for (int i=0;i<n;i++){
        if (rnk[i]==0){height[rnk[i]]=k=0;continue;}
        if (k) k--;
        int j = sa[rnk[i]-1];
        while (i+k<n && j+k<n && s[i+k]==s[j+k]) k++;
        height[rnk[i]] = k;
    }
}
 
int main()
{
    int n;
    while (~scanf("%s",s)){
        int len = strlen(s);
        scanf("%s",s+len+1);
        n = len + strlen(s+len+1) + 1;///+1很关键,虽然这题数据太水不+1也会AC
        build(n,200);//debug(1);
        getheight(n);//debug(2);
        int ans = 0;
        for (int i=1;i<n;i++)
            if ( (sa[i]<len && sa[i-1]>len) || (sa[i]>len && sa[i-1]<len) )
                ans = max(height[i],ans);
        //debug(3);
        printf("%d\n",ans);
    }
    return 0;
}

后缀数组查询两段区间LCP,ST维护height

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#define debug(x) printf("----line %s----\n",#x)
using namespace std;

const int N = 2e5 + 5;

int sa[N],wa[N],wb[N],rnk[N],cnt[N],height[N];
char s[N];
int st[N][20];

void buildsa(int n,int m)///m是初始字符种类,可以偏大
{
    int *x = wa,*y = wb,p,i,j;
    for (int i=0;i<m;i++) cnt[i] = 0;
    for (int i=0;i<n;i++) cnt[x[i]=s[i]]++;
    for (int i=1;i<m;i++) cnt[i] += cnt[i-1];
    for (int i=n-1;i>=0;i--) sa[--cnt[x[i]]] = i;

    for (j=1,p=1;p<n;j<<=1,m=p){
        for (p=0,i=n-j;i<n;i++) y[p++] = i;
        for (int i=0;i<n;i++) if (sa[i]>=j) y[p++] = sa[i]-j;///得出根据第二关键字排名的

        for (int i=0;i<m;i++) cnt[i] = 0;
        for (int i=0;i<n;i++) cnt[x[y[i]]]++;
        for (int i=1;i<m;i++) cnt[i] += cnt[i-1];
        for (int i=n-1;i>=0;i--) sa[--cnt[x[y[i]]]] = y[i];
    
        swap(x,y);
        x[sa[0]] = 0;
        p = 1;
        for (i=1;i<n;i++)
            x[sa[i]] = ( y[sa[i]]==y[sa[i-1]] && y[sa[i]+j]==y[sa[i-1]+j] )? p-1:p++;
    }
}

void getheight(int n)
{
    int 
  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值