P6114 【模板】Lyndon 分解

P6114 【模板】Lyndon 分解

冷门的板子题,居然在 h d u hdu hdu多校出了。

搞一波,这里不讲解原理,只讲实现。


将字符串分为三个部分: s 1 s 2 s 3 s_1s_2s_3 s1s2s3

s 1 : s_1: s1:已经进行处理好的 L y n d o n Lyndon Lyndon串。

s 2 : s_2: s2:正在处理的 L y n d o n Lyndon Lyndon近似串。

s 3 : s_3: s3:未处理的串。

1.预备工作,三个指针 i , j , k i,j,k i,j,k

i : i: i:指向 s 2 s_2 s2的首字符。

j : j: j:指向 s 2 s_2 s2当前考虑的字符,也即 k k k上一个循环所对应的字符。

k : k: k: s 3 s_3 s3的首字符。


2.遍历所有字符,初始化 i = 1 , j = i , k = i + 1 i=1,j=i,k=i+1 i=1,j=i,k=i+1

s [ j ] < s [ k ] s[j]s[j]<s[k]说明必须更新,即连接 s 2 s [ k ] s_2s[k] s2s[k],所以 j = i , k + + j=i,k++ j=i,k++

s [ j ] = = s [ k ] s[j]==s[k] s[j]==s[k]说明可能被更新,直接 j + + , k + + j++,k++ j++,k++.

s [ j ] > s [ k ] s[j]>s[k] s[j]>s[k],说明此时必须开始分离 L y d o n Lydon Lydon串。

i i i开始,每个长度 k − j k-j kj的串( k − j k-j kj是循环节)都是 L y d o n Lydon Lydon串,加入到 s 1 s_1 s1中,然后 i + = ( k − j ) i+=(k-j) i+=(kj),直到 i > j i>j i>j

时间复杂度: O ( n ) O(n) O(n)

#include
using namespace std;
typedef long long ll;
const int N=5e6+5,M=2e4+5,inf=0x3f3f3f3f,mod=1e9+7;
#define mst(a) memset(a,0,sizeof a)
#define lx x<<1
#define rx x<<1|1
#define reg register
#define PII pair
#define fi first
#define se second
#define pb push_back
char s[N];
int ans=0; 
int main(){
	scanf("%s",s+1);int n=strlen(s+1),i=1;
	while(i<=n){
		int j=i,k=i+1;
		while(k<=n&&s[j]<=s[k]){
			s[j]<s[k]?j=i:j++;
			k++;
		}
		while(i<=j){
			ans^=(i+(k-j)-1);
			i+=k-j;
		}
	}
	printf("%d\n",ans);
	return 0;
}

注释版:

#include
#include
#include
using namespace std;
char s[5000005];
int n,ans;
int main()
{
	scanf("%s",s+1);
	n=(int)strlen(s+1);
	for(int i=1;i<=n;)	//i表示s2的首字符. 
	{
		int j=i,k=i+1;//初始化,j指向s2中我们当前考虑的字符即k在s2的上一个循环中对应的字符,k指向s3的首字符. 
		while(k<=n&&s[j]<=s[k])
		{
			if(s[j]<s[k])j=i;//s2s[k]成为一个Lydon串,于是我们让k自增,j指向s2的首字符,合并为一整个
			else j++;//s[k]添加到s2末尾不会影响近似简单性,直接让j,k自增即可,保持循环不变式
			k++;
		}
		while(i<=j)//s2s[k]不是一个近似简单串,s2要分解出一个Lydon子串,从v的开头重新开始
		{
			ans^=i+k-j-1;	//s[i....i+(k-j)-1] 长度为k-j. 
			i+=k-j;//s2的首字符变为i+k-j 
		}
	}
	printf("%d\n",ans);
	return 0;
}

你可能感兴趣的:(Lyndon分解,字符串,后缀数组)