字符串-最小表示法

1.问题描述

给出一个长度为 n n n的字符串 S S S,其第 i i i个字符为 S i ( 0 < = i < n ) S_i(0<=i<n) Si(0<=i<n)
可以重复执行操作:移除第 0 0 0个字符,并将其插入到第 n − 1 n-1 n1个位置。
找出所有可以通过上述操作得到的字符串中字典序最小的字符串,这个字典序最小的字符串被称为字符串 S S S的最小表示。

2.算法流程

1.定义两个指针 i , j i,j i,j,定义变量 k k k,初始时,让 i i i指向位置 0 0 0 j j j指向位置 1 1 1 k = 0 k=0 k=0
2.比较第 i + k i+k i+k和第 j + k j+k j+k个字符,
(这里默认为 x + k x+k x+k ( x + k ) m o d    n (x+k)\mod n (x+k)modn x = i , j x=i,j x=i,j
S i + k = S j + k S_{i+k}=S_{j+k} Si+k=Sj+k,让 k = k + 1 k=k+1 k=k+1
S i + k > S j + k S_{i+k}>S_{j+k} Si+k>Sj+k,让 i = i + k i=i+k i=i+k k = 0 k=0 k=0
S i + k < S j + k S_{i+k}<S_{j+k} Si+k<Sj+k,让 j = j + k j=j+k j=j+k k = 0 k=0 k=0
3.若 i = j i=j i=j,让 i = i + 1 i=i+1 i=i+1
4.若 i > = n ∣ ∣ j > = n ∣ ∣ k > = n i>=n||j>=n||k>=n i>=nj>=nk>=n m i n ( i , j ) min(i,j) min(i,j)即为字典序最小的字符串的开头。否则,回到2。

3.证明

1.当 k = n k=n k=n,因为算法保证 i ! = j i!=j i!=j,则可以证明字符串只由一个字符组成,那么任意 i , j i,j i,j都为最小表示。
2.当 S i + k ! = S j + k S_{i+k}!=S_{j+k} Si+k!=Sj+k,不妨假设 S i + k > S j + k S_{i+k}>S_{j+k} Si+k>Sj+k,则第 i , i + 1 , . . . , i + k i,i+1,...,i+k i,i+1,...,i+k个位置一定不是字典序最小的字符串,设第 p p p个位置为最小字符串 ( i < = p < = i + k ) (i<=p<=i+k) (i<=p<=i+k),则有
S p S p + 1 . . . S i + k − 1 = S j + p − i S j + p − i + 1 . . . S j + k − 1 S_pS_{p+1}...S_{i+k-1}=S_{j+p-i}S_{j+p-i+1}...S_{j+k-1} SpSp+1...Si+k1=Sj+piSj+pi+1...Sj+k1 S i + k > S j + k S_{i+k}>S_{j+k} Si+k>Sj+k
j , j + 1 , . . . , j + k j,j+1,...,j+k j,j+1,...,j+k中存在对应比其字典序更小的字符串。
3.又 i , j i,j i,j会把所有下标都扫描到,当 i > = n ∣ ∣ j > = n i>=n||j>=n i>=nj>=n又必有 i , j i,j i,j中某个指针的值小于 n n n,而另一指针一定由于操作 2 2 2或操作 3 3 3而移动,故值小于 n n n的指针则为最小表示字符串的开头。

4.复杂度

时间复杂度 O ( n ) O(n) O(n),空间复杂度 O ( n ) O(n) O(n),证明略。

5.代码

P1368 工艺 /【模板】最小表示法

#include<bits/stdc++.h>
using namespace std;
const int N=3e5+5;
int n,a[N];
int sol(int n,int a[N])
{
    if(n==1) return 0;
    int i=0,j=1,k=0;
    while(i<n&&j<n&&k<n)
    {
        if(i==j) i++;
        else if(a[(i+k)%n]==a[(j+k)%n]) k++;
        else if(a[(i+k)%n]>a[(j+k)%n]) i+=k+1,k=0;
        else j+=k+1,k=0;
    }
    return min(i,j);
}
int main()
{
    scanf("%d",&n);
    for(int i=0;i<n;i++) scanf("%d",&a[i]);
    int ans=sol(n,a);
    for(int i=0;i<n;i++)
        printf(i==n-1?"%d\n":"%d ",a[(i+ans)%n]);
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值