冷门的板子题,居然在 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 k−j的串( k − j k-j k−j是循环节)都是 L y d o n Lydon Lydon串,加入到 s 1 s_1 s1中,然后 i + = ( k − j ) i+=(k-j) i+=(k−j),直到 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;
}