【XSY3905】字符串题(lyndon串,构造)

题面

字符串题

题解

设所有长度不超过 n n n 的串的集合为 S S S

考虑找到一种方法,能够对一个 lyndon 串 A A A ,直接求出 A A A 的下一个 lyndon 串。方法如下:

  1. 先将 A A A 不断复制, 取出前 n n n 位作为新的 A A A ,即 A ← A A A ⋯ A\leftarrow AAA⋯ AAAA 的前 n n n 位。
  2. 如果 A A A 的最后一位是 ′ a ′ + m − 1 'a'+m-1 a+m1,即字符集中最大的字符,则将其删去,一直删除直到最后一位不为 ′ a ′ + m − 1 'a'+m-1 a+m1
  3. A A A 的最后一个字符变为这个字符在字符集中的后继。(其实 2、3 操作的实质是找到 1 操作后的 A A A 在字典序中的下一个字符串。

设构造出的串为 B B B。证明这种方法的正确性,只需要证明 B B B 是 lyndon 串,且 B B B S S S 中字典序大于 A A A 的最小的 lyndon 串。

  • 先证 B B B 为 lyndon 串:

    根据构造方式,设 A = a 1 a 2 ⋯ a ∣ A ∣ A=a_1a_2⋯a_{|A|} A=a1a2aA。显然可以发现 B B B A A ⋯ A a 1 a 2 ⋯ a x ( a x + 1 + 1 ) AA⋯Aa_1a_2⋯a_x(a_{x+1}+1) AAAa1a2ax(ax+1+1) 的形式,其中 1 ≤ x < ∣ A ∣ 1\leq x<|A| 1x<A。由于 A A A 为 lyndon 串,所以对 ∀ 1 < i ≤ ∣ A ∣ \forall 1<i≤|A| 1<iA,有 a i a i + 1 ⋯ a ∣ A ∣ a 1 a 2 ⋯ a i − 1 > a 1 a 2 ⋯ a ∣ A ∣ a_ia_{i+1}\cdots a_{|A|}a_1a_2⋯a_{i−1}>a_1a_2⋯a_{|A|} aiai+1aAa1a2ai1>a1a2aA

    不难发现 B B B 的循环移位中 B B B 为其中的严格最小值,所以 B B B 为 lyndon 串。

  • 再证 A , B A,B A,B 之间没有 l y n d o n lyndon lyndon 串:

    如果有一个 S S S 中的串 T T T 字典序在 A , B A,B A,B 之间且为 lyndon 串,由构造方法可以知道 A < T < A A A ⋯ A<T<AAA⋯ A<T<AAA

    T = A A ⋯ A T ′ T=AA⋯AT′ T=AAAT,其中 T ′ T' T 的前 ∣ A ∣ |A| A 位不等于 AA ,显然有 T ′ < A T'<A T<A,则以 T ′ T' T 开头的循环移位小于 T T T ,与 T T T 是 lyndon 串矛盾。

由于大部分满足条件的 lyndon 串的长度均为 n n n,这个算法均摊复杂度为 O ( 1 ) O(1) O(1),可以 O ( x ) O(x) O(x) 通过此题。

代码如下:

#include<bits/stdc++.h>
 
#define S 35
#define N 200010
 
using namespace std;
 
inline int read()
{
    int x=0,f=1;
    char ch=getchar();
    while(ch<'0'||ch>'9')
    {
        if(ch=='-') f=-1;
        ch=getchar();
    }
    while(ch>='0'&&ch<='9')
    {
        x=(x<<1)+(x<<3)+(ch^'0');
        ch=getchar();
    }
    return x*f;
}
 
struct Query
{
    int x,id;
}q[N];
 
bool operator < (Query a,Query b)
{
    return a.x<b.x;
}
 
int n,m,Q;
int len;
char s[S],Ans[N][S];
 
int main()
{
    n=read(),m=read(),Q=read();
    for(int i=1;i<=Q;i++)
        q[i].x=read(),q[i].id=i;
    sort(q+1,q+Q+1);
    len=1;
    s[0]='a';
    int tmp=1;
    while(tmp<=Q&&q[tmp].x==1)
    {
        for(int i=0;i<len;i++)
            Ans[q[tmp].id][i]=s[i];
        tmp++;
    }
    for(int i=2;i<=q[Q].x;i++)
    {
        int nowl=len;
        while(1)
        {
            for(int i=0;i<nowl&&len<n;i++)
                s[len++]=s[i];
            if(len==n) break;
        }
        while(len>0&&s[len-1]=='a'+m-1) len--;
        s[len-1]++;
        while(tmp<=Q&&q[tmp].x==i)
        {
            for(int i=0;i<len;i++)
                Ans[q[tmp].id][i]=s[i];
            tmp++;
        }
    }
    for(int i=1;i<=Q;i++)
    {
        for(int j=0;Ans[i][j];j++)
            putchar(Ans[i][j]);
        puts("");
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值