【杭电多校2020】Minimum Index【Lyndon Word】

题意:给定字符串,求所有前缀的最小后缀。

n ≤ 2 × 1 0 7 n\leq 2\times10^7 n2×107

最小后缀就是Lyndon分解的最后一段。而Duval本质上是可以重复修改的增量算法,所以是可以做的。

a n s i ans_i ansi为前缀 i i i的最小后缀。设维护未确定的循环节的指针为 i , j , k i,j,k i,j,k,即 S i . . . k − 1 = t + t + . . . + t + t 1 S_{i...k-1}=t+t+...+t+t_1 Si...k1=t+t+...+t+t1,其中 t t t为Lyndon Word, t 1 t_1 t1为其可空后缀, j = k − ∣ t ∣ j=k-|t| j=kt

S j = S k S_j=S_k Sj=Sk时,根据意识流,前缀 j j j的最小后缀一定在 [ i , j ] [i,j] [i,j]内。因为是个循环,所以 a n s k = a n s j + k − j ans_k=ans_j+k-j ansk=ansj+kj

S j < S k S_jSj<Sk,令 j = i j=i j=i,此时 S j . . . k S_{j...k} Sj...k是个Lyndon Word,所以 a n s k = j ans_k=j ansk=j

S j > S k S_j>S_k Sj>Sk,相当于 t 1 t_1 t1这一段的 a n s ans ans都是假的,需要重新计算,不用管。但 ∣ t 1 ∣ = 1 |t_1|=1 t1=1的时候会出一些奇怪的问题,需要把 a n s k ans_k ansk的值算出来,为 i i i k k k

复杂度 O ( n ) O(n) O(n)

#include 
#include 
#include 
#include 
#define MAXN 20000005
using namespace std;
char s[MAXN];
int ans[MAXN];
const int MOD=1e9+7;
int main()
{
	int T;
	scanf("%d",&T);
	while (T--)
	{
		scanf("%s",s+1);
		int n=strlen(s+1);
		for (int i=1;i<=n;i++) ans[i]=0;
		ans[1]=1;
		for (int i=1;i<=n;)
		{
			int j=i,k=i+1;
			ans[k]=k;
			while (s[j]<=s[k])
			{
				if (s[j]==s[k]) ans[k]=ans[j]+k-j,++j;
				else ans[k]=j=i;
				++k;
			}
			while (i<=j) i+=k-j;
			ans[k]=i;
		}
//		for (int i=1;i<=n;i++) printf("%d%c",ans[i]," \n"[i==n]);
		int sum=0;
		for (int i=1,x=1;i<=n;i++,x=x*1112ll%MOD) sum=(sum+1ll*ans[i]*x)%MOD;
		printf("%d\n",sum);
	}
	return 0;
}

你可能感兴趣的:(【杭电多校2020】Minimum Index【Lyndon Word】)