字符串大师

字符串大师

题意

一个串 TS 的循环节,当且仅当存在正整数 k,使得 STkT 重复 k 次)的前缀。

给出字符串 S 每个前缀的最短循环节,求出字典序最小的 S

思路

记字符串 S 的最短循环节为 d,有:

len(S)border(S)=d

证明:

先证明引理:对于循环节 d[1,len(S)d]=[d,len(S)]

分类讨论,若循环节为完全循环,引理显然成立。

若循环节为不完全循环,记 len(S)modd=p

[d,len(S)] 缺失的 pd 个字符位于 [len(S)d+1,len(S)p]

[1,len(S)d] 同时也缺失了这一部分的字符。综上,引理得证。

根据引理:对于循环节 d[1,len(S)d]=[d,len(S)]

再加上 d 为最短循环节,所以 [1,len(S)d],[d,len(S)] 都是 border(S),得证。

所以可以先用 len(S)d 算出 border(S),再通过反向的 KMP 还原字符串。

考虑 KMP 求 border 的过程:

首先令 border(1)=0

对于 i[2,n]

t=border(i1),重复 tborder(t) 直到 t=0St+1=Si

若最后 t=0border(i)=0,否则 border(i)=t+1

反过来同理。

首先令 S1=A,因为要满足字典序最小。

对于 i[2,n]

t=border(i1),重复 tborder(t) 直到 t=0t+1=border(i)

每次 tborder(t) 前,根据 KMP 求 border 的过程可知,St+1Si

若最后 t0,可知 St+1=Si,直接赋值即可。

t=0,可通过上过程得到的若干组不等关系找出一个最小满足条件的 Si

时间复杂度:O(n)

代码

#include <bits/stdc++.h>
using namespace std;

const int N = 1e5 + 5;

int n, fail[N];
char s[N];
bool neq[30];

int main() {
	cin >> n;
	for (int i = 1; i <= n; i ++) {
		cin >> fail[i];
		fail[i] = i - fail[i];
	}
	s[1] = 'a';
	for (int i = 2, t; i <= n; i ++) {
		memset(neq, 0, sizeof(neq));
		for (t = fail[i - 1]; fail[i] != t + 1; t = fail[t]) {
			neq[s[t + 1] - 'a'] = 1;
			if (!t) break;
		}		
		if (fail[i] == t + 1) s[i] = s[t + 1];
		else {
			for (char x = 'a'; x <= 'z'; x ++) {
				if (!neq[x - 'a']) {
					s[i] = x;
					break;
				}
			}
		}
	}
	puts(s + 1);
	return 0;
}
posted @   maniubi  阅读(11)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】
点击右上角即可分享
微信分享提示