目录
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);
}