【ICPC】【2021沈阳站】String Problem(Lyndon分解)

传送门:String Problem        Lyndon分解 


JB hates solving string problems. Therefore, when his friend Potato gives him a string problem to solve, he immediately gives it to you and continues playing Genshin Impact, the greatest game in the world.

You are given a string and then for each nonempty prefix, you need to find the largest substring in lexicographical order and point out the leftmost occurrence of the largest substring.


Input

The only line contains a string S (1≤∣S∣≤1e6)(1≤∣S∣≤1e6), which consists of lowercase Latin letters, a to z.


Output

Output ∣S∣ lines, the i-th of which contains two integers li​ and ri​, indicating the leftmost occurrence of the largest substring in the prefix of length i.


题目大意

JB讨厌解决字符串问题。因此,当他的朋友土豆给他一个字符串问题要解决时,他会立即把它给你,并继续玩Genshin Impact,世界上最伟大的游戏。

给定一个字符串,然后对于每个非空前缀,您需要按照字典顺序找到最大的子串,并指出最大子串的最左边位置。

例如对于样例1的第二个位置'o',依题目要求有两个字串“po"和"o",字典序更大的是"po",则该子串的范围为[1,2]因此输出其左右边界1,2.

总结:题目要求处理一个字符串 S,并对于每一个非空前缀,找出该前缀中按字典序排列的最大子字符串,并指出这个最大子字符串的左、右区间的位置。


题解

发现这道题很像Lyndon分解的形式,但是题目要求的是求出所有前缀中的最大子字符串。所以考虑Lyndon的逆用,就是只改变求Lyndon分解串中的比较过程,把大于变成小于,小于变成大于即可。

下面是代码详解。

  1. 输入处理:程序首先从标准输入中读取一个字符串 S,并计算该字符串的长度 n

  2. 初始化:定义一个整型数组 ans,大小为 n,用于存储每个字符的最小划分长度。数组的初始值为 0,表示尚未被赋值。

  3. Duval 算法

    • 算法通过两个指针 i 和 k 来遍历字符串。i 表示当前处理的起始位置,j 是一个辅助变量,用于标记字符串的基本重复段。
    • 从 i 开始向后扫描,找到所有和 s[i] (当前字符) 相关的字符,以确定它们是否属于同一个划分。
    • 每当找到一个字符与 s[j] 相等,且没有被赋值 ans[k],就将其赋值为 i + 1,即当前划分的起始位置。
    • 如果发现 s[k] < s[j],则表示出现了新的小字母,更新 j 到 i,然后增加 j
    • 最后,通过一个循环while(i <= j)来更新 i,使其跳过已处理的部分。
#include <iostream>
#include <vector>
#include <cstring>
using namespace std;
char S[(int)1e6+5];
vector<int>gap;
int ans[(int)1e6+5];
void Duval(char *s,int n){
    for(int i=0,j,k;i<n;){
        if(!ans[i])ans[i]=i+1;
        for(j=i,k=i+1;k<n&&s[k]<=s[j];k++){
            if(!ans[k])ans[k]=i+1;
            if(s[k]<s[j])j=i;
            else ++j;
        }
        while(i<=j){
            i+=k-j;
        }
    }
}
int main(){
    scanf("%s",S);
    int n=strlen(S);
    Duval(S,n);
    for(int i=0;i<n;i++){
        cout<<ans[i]<<" "<<i+1<<endl;
    }
    return 0;
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值