最小表示法的学习

本文介绍了一种用于确定循环字符串最小表示的方法——最小表示法,并详细解释了其背后的逻辑与实现步骤。通过设置两个指针u和v进行比较,最终找到最小的循环起始位置。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

只能说我刚学完最小表示法,现在写一下自己的感想,因为不懂怎么用csdn,所以只有文字描述和代码

#include 
#include 
#include 
#include 
#include 
using namespace std;
///最小表示法
/*
最小表示法就是表示一个循环的字符串的最小表示
例如:
字符串cbabc的最小表示为abccb,就是从第3个字母开始

现在使用两个变量 u,v(无论如何确保 u < v)表示在比较的开始位置,字符串为s,长度为n
使用k表示从u和v开始的第k个位置的字母(每次比较k从0开始)
如果s[(u + k)%n] == s[(v + k)%n], 那么k++
如果k == n,那么直接返回u或则v
当s[(u + k)%n] < s[(v + k)%n] 时,那么就表示u开头的字母比较小 v就可以直接等于v + k + 1
为什么不能是v ~ v + k + 1之间的一个数呢,其实很简单,
因为当我们随意选择v ~ v + k + 1其中一个数为字符串的开始位置是时,
假设这个数为v + i,那么以u + i为开始的字符串必然小于v + i的字符串
而且u + i为开头的字符串也必然小于以u开头的字符串(
证:假设u + i <= v,那么我们可以知道 u < v,而且以u~v之间的数开头的字符串必然小于以u开头的字符串
因为如果中间有比u小的字符串,那么v是必然经过这个数的
当u + i > v时,你可以知道u + i的字符串必然小于u+i-(v-u)的字符串,而u+i-(v-u)的字符串又小于u的字符串
所以 v就可以直接等于v + k + 1
当s[(u + k)%n] > s[(v + k)%n] 时,我们可以知道v的字符串比较小
因为以u~v之间的一个数开头的字符串必然小于以u开头的字符串,而u的字符串小于v的字符串
所以如果u + k + 1 <= v, 那么u 直接可以等于v + 1
同理,如果u + k + 1 > v,那么u 直接可以等于u + k + 1
以上
*/
const int M = 1e5 +5;
char s[M];
int n;
int min_string(){
    int u = 0, v = 1;//表示比较的串的开始位置
    while(u < n && v < n){
        int k = 0;
        while(k < n && s[(u + k)%n] == s[(v + k)%n]) k++;
        if(k == n) return u;
        if(s[(u + k)%n] < s[(v + k)%n]) v = v + k + 1;
        else{
            if(u + k + 1 <= v) u = v + 1;
            else u = u + k + 1;
        }
        if(u > v) swap(u,v);
    }
    return u;
}
//
int main(){
    int _;
    scanf("%d",&_);
    while(_--){
        scanf("%d%s",&n,s);
        printf("%d\n",min_string());
    }
    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值