zoukankan      html  css  js  c++  java
  • LOJ129 Lyndon 分解

    Lyndon 分解

    样例

    样例输入 1
    ababa
    样例输出 1
    2 4 5

    样例输入 2
    bbababaabaaabaaaab
    样例输出 2
    1 2 4 6 9 13 18

    样例输入 3
    azAZ0129
    样例输出 3
    2 4 8

    数据范围与提示

    (1le |s| le 2^{20})

    OZY的题解

    冷门东西,但是今天考到了,做个记录。

    记号

    (s[l : r]) 表示字符串(s) 从第(l) 个字符到第(r) 个字符的子串(从(1) 开始标号),(|s|) 表示(s) 的长度。

    (l = 1)(s[l : r]) 简写为(s[: r]) ,表示(s) 的一个前缀。当(r = |s|)(s[l : r]) 简写为(s[l :]) ,表示(s) 的一个后缀。

    (st, s +t) 表示两个字符串(st) 的拼接,(s^k) 表示(k)(s) 拼起来,特别地,(s^{infty}) 表示(s) 的无限循环。

    定义

    Lyndon 串:如果一个串(s) 满足(s = min{s[i :]|1 le i le |s|}) 那么我们称串(s) 为Lyndon 串。定义字符串的大小关系就是字典序的大小关系

    性质

    (u,v)均为Lyndon Words,且(u<v),那么(uv)也是一个Lyndon Words。

    证明还是比较显然的,这里就不证了

    Lyndon 划分

    对于一个字符串(s),如果一个划分将它分成若干个串(s=p_1+p_2+p_3+dots+p_n),使得每个(p)都是Lyndon Words,且(p_ige p_{i+1}),则这个划分是Lyndon 划分

    可以发现,一个字符串,一定存在一种Lyndon 划分,证明可以用构造法来证明。

    一开始先所有(p)设为单个字母。显然,这是满足第一个条件的,只需要再满足递减的关系就可以了。

    可以发现若(p_i<p_{i+1}),它们合起来也是一个Lyndon Words。

    并且可以发现,对于一个串,他的Lyndon 划分是唯一的。

    算法

    目的是求出(r[i]),表示第(i)个字符所属Lyndon Words的右端点的下一个位置。

    就是维护类似单调栈的东西就可以了。单调栈内维护的是属于同一Lyndon Words的节点,换句话说如果不满足字典序的单调递增,就要清空。发现这就是维护定义……很显然啊。

    复杂度瓶颈在于比较后缀大小,用后缀树(DC3后缀数组+笛卡尔树)和±1RMQ即可(O(n))。这里只给出不能AC的hash做法,(O(nlog n))

    当然这题还有(O(n))的Duval算法,但是我觉得没必要学。

    #include<bits/stdc++.h>
    #define co const
    #define il inline
    using namespace std;
    typedef unsigned long long ULL;
    
    co int N=(1<<20)+10;
    co ULL base=131;
    char str[N];int n;
    ULL pw[N],hs[N];
    
    il ULL calc(int l,int r){
    	return hs[r]-hs[l-1]*pw[r-l+1];
    }
    int lcp(int x,int y){ // str[x:],str[y:]
    	int l=0,r=n-max(x,y)+1;
    	while(l<r){
    		int mid=(l+r+1)>>1;
    		if(calc(x,x+mid-1)==calc(y,y+mid-1)) l=mid;
    		else r=mid-1;
    	}
    	return l;
    }
    il bool cmp(int x,int y){ // str[x:]<str[y:]
    	int len=lcp(x,y);
    	if(len==n-max(x,y)+1) return x>y; // partition by >=
    	return str[x+len]<str[y+len];
    }
    
    int r[N],st[N],top;
    int main(){
    	scanf("%s",str+1),n=strlen(str+1);
    	pw[0]=1;
    	for(int i=1;i<=n;++i){
    		pw[i]=pw[i-1]*base;
    		hs[i]=hs[i-1]*base+str[i];
    	}
    	for(int i=1;i<=n;++i){
    		while(top&&cmp(i,st[top])) r[st[top--]]=i;
    		st[++top]=i;
    	}
    	while(top) r[st[top--]]=n+1;
    	for(int i=1;i<=n;i=r[i]) printf("%d ",r[i]-1);
    	return 0;
    }
    
  • 相关阅读:
    TIM时钟频率计算
    时钟节拍tick
    Continue作用
    struct结构体的字节长度,字节对齐
    IAR所包含的头文件位置
    Oracle存储过程给变量赋值的方法
    DataTable如何去除重复的行
    C#遍历窗体所有控件或某类型所有控件
    SqlServer无备份下误删数据恢复
    45.4.7 序列:USER_SEQUENCES(SEQ)
  • 原文地址:https://www.cnblogs.com/autoint/p/lyndon_words.html
Copyright © 2011-2022 走看看