字符串-Runs学习笔记

runs学习笔记

学习链接

定义

Run

有一个三元组\(run=(l,r,p)\),其中\(l,r\)表示在字符串的\(s[l,r]\)区间,\(p\)表示在\(s[l,r]\)中字符串的字串的最小循环节

注意:

  1. 不存在扩展性,也就是说如果成立的话整个三元组会整体右移或者左移,或者向两边同时扩展
  2. \(2p\leq r-l+1\)

其中run的指数(即循环串出现的次数)\(e_r=\frac{r-l+1}{p}\)

\(\rho_{run}(n)\)表示长度为\(n\)的字符串中其子串能够构成三元组\(Run\)的个数

\(\sigma_{run}(n)\)表示长度为n的字符串的子串构成的三元组的指数和最大 最大的原因是因为p的定义是最小循环节 ,即\(\sum_{k=1}^{t}\frac{r_k-l_k+1}{p_k}\),其中\(t\)为三元组个数(不就是\(\rho_{run}(n)\)吗)

Lyndon根

在三元组\(run=(l,r,p)\)表示的区间里面,长度为\(p\)的Lyndon串

run的所有循环节中最小表示的的那一个

一个Run里面最少存在一个Lyndon根,证明显然

\(run=(l,r,p)\)的lyndon根\(s[u,u+p-1]\)满足\(u!=l\),那么其为真Lyndon根

重载大小运算

\(<_0\):在\(<_0\)\(a<b\),记作\(a<_0b\)

\(<_1\):在\(<_1\)\(b<a\),记作\(b<_1a\)

其中\(a,b\)均为字符

Lyndon数组

\(l_t[i]\)表示在\(<_t\)的情况下,左端点为\(i\)的最长Lyndon串的右端点

性质

性质1:周期相同的run的交长度小于p

证明:若大于p,在交集中可以选择一个循环节进行扩展,即为上述不存在扩展性

性质2:\(l_0[i]\)\(l_1[i]\)都存在一个\(i\)的值等于\(i\),位置不重合

证明显然

性质3:对于一个三元组\(Run=(l,r,p)\),假设\(s[r+1]<_ts[r+1-p]\),那么任意Lyndon根s[u,u+p-1]都有\(l_t(u)=u+p-1\)

证明:对于一个\(s[u,v],u+p-1<v\leq j\),满足其形式为\(w^k+w^`\),对于\(v>j\),不存在Lyndon串

性质4:\(s[u,l_0(u)],s[u,l_1(u)]\)不可能同时为两个\(run\)的Lyndon根

证明:设\(l_0(u)=u\),那么有\(s[u]=s[u-1]+s[u+p-1]=s[l1(u)]\),故\(s[u,l_1(u)]\)不是Lyndon串

性质5:任意两个不同的run的真Lyndon根的左端点集合不相交

证明见上

Runs定理

Runs定理如下:

\(\rho_{run}(n)<n\),\(\sigma_{run}(n)<3n\)

证明:

设B(r)表示三元组\(Run\)的所有真Lyndon串的左端点集合,有任意\(B(r_1)\cap B(r_2)\),所以\(\sum_rB(r)\leq n-1\),又因为\(B(r)\geq 1\)所以\(\rho_{run}(n)<n\)

\(r=(l,r,q)\)循环了x次,其真Lyndon根至少有\(x-1\)个,故\(|B(r)|>e_r-2,\sum_r(e_r-2)<=n-1\),所以\(\sigma_{run}(n)<3n\)

Runs的求解

算法1

枚举周期\(p\),然后在串中间打标记(每隔长度p打一次),大小为p的run一定会穿过两个点,相邻两个点之间求lcp,如果能够将范围连起来就找到一个

【集训队作业2018】串串划分

struct STRUCT{
    ll sa[M],rk[M],hei[M],lim=125;
    ll num[M],x[M],y[M];
    ll lg[M],st[M][21];
    void init_sa(char *s){
        lim=125;
        for(ll i=1;i<=n;i++)num[x[i]=s[i]]++;
        for(ll i=2;i<=lim;i++)num[i]+=num[i-1];
        for(ll i=n;i>=1;i--)sa[num[x[i]]--]=i;
        for(ll k=1;k<=n;k<<=1){
            ll sum=0;
            for(ll i=n-k+1;i<=n;i++)y[++sum]=i;
            for(ll i=1;i<=n;i++)if(sa[i]>k)y[++sum]=sa[i]-k;
            for(ll i=1;i<=lim;i++)num[i]=0;
            for(ll i=1;i<=n;i++)num[x[i]]++;
            for(ll i=1;i<=lim;i++)num[i]+=num[i-1];
            for(ll i=n;i>=1;i--){
                sa[num[x[y[i]]]--]=y[i];
                y[i]=0;
            }
            swap(x,y);
            x[sa[1]]=sum=1;
            for(ll i=2;i<=n;i++){
                x[sa[i]]=(y[sa[i]]==y[sa[i-1]]&&y[sa[i]+k]==y[sa[i-1]+k])?sum:++sum;
            }
            if(sum==n)break;
            lim=sum;
        }
        for(ll i=1;i<=n;i++)rk[sa[i]]=i;
        for(ll i=1,k=0;i<=n;i++){
            if(rk[i]==1)continue;
            if(k)k--;
            ll pos=sa[rk[i]-1];
            while(i+k<=n&&pos+k<=n&&s[i+k]==s[pos+k])k++;
            hei[rk[i]]=k;
        }
    }
    void init_st(){
        for(ll i=2;i<=n;i++)lg[i]=lg[i>>1]+1;
        for(ll i=1;i<=n;i++)st[i][0]=hei[i];
        for(ll j=1;j<=19;j++){
            for(ll i=1;i+(1<<j)-1<=n;i++){
                st[i][j]=min(st[i][j-1],st[i+(1<<(j-1))][j-1]);
            }
        }
    }
    ll ask(ll l,ll r){
        l=rk[l],r=rk[r];
        if(l>r)swap(l,r);
        ll k=lg[r-l];
        return min(st[l+1][k],st[r-(1<<k)+1][k]);
    }
}t1,t2;
ll lcs(ll x,ll y){
    return t2.ask(n-x+1,n-y+1);
}
ll lcp(ll x,ll y){
    return t1.ask(x,y);
}
bool compare(ll x,ll y){
    ll len=lcp(x,y);
    return s[x+len]<s[y+len];
}
void get_runs(ll fg){
    for(ll i=n-1;i>=1;i--){
        ly[i]=i+1;
        while(ly[i]&&compare(ly[i],i)==fg)ly[i]=ly[ly[i]];
    }
    for(ll len=1;len<=n;len++){
        if(!ly[len])continue;
        ll x=len,y=ly[len],p=y-x;
        ll Lcs=lcs(x,y),Lcp=lcp(y,x);
        ll l=x-Lcs+1,r=y+Lcp-1;
        if(Lcs+Lcp>p&&st.insert(r*mod+l).second){//覆盖长度,满足条件
            for(ll i=l-1;i<l+2*p-1&&i+2*p<=r;i++){
                len_run[++cnt]=p;
                for(ll j=i+2*p;j<=r;j+=2*p)id[j].push_back(cnt);
            }
        }
    }
}

时间复杂度:\(n(log(n))\)

算法2

由于run的Lyndon根一定是形如\(s[i,l_t(i)]\),所以先求出Lyndon数组在进行扩展,求 Lyndon 数组可以考虑一种从后向前构造 Lyndon 分解的方法,即从后往前扫,维护一个单调栈,每次加入一个字符,检查能不能和栈顶的串合并(我不会写)

时间复杂度:\(n(log(n))\)

结尾

写的太干涩了,我估计不会看第二遍这个东西,自己边看边写都要写吐了

posted @ 2023-04-30 18:42  6penny  阅读(207)  评论(0编辑  收藏  举报