BZOJ4974 字符串大师【KMP】【贪心】

xiaoxiao2025-08-28  19

题意:

一个串T是S的循环节,当且仅当存在正整数k,使得S是T^k(即T重复k次)的前缀,比如abcd是abcdabcdab的循环节。给定一个长度为n的仅由小写字符构成的字符串S,请对于每个(1<=k<=n),求出S长度为k的前缀的最短循环节的长度per_i。字符串大师小Q觉得这个问题过于简单,于是花了一分钟将其AC了,他想检验你是否也是字符串大师。小Q告诉你 n n n以及 p e r 1 , p e r 2 , . . . , p e r n per_1,per_2,...,per_n per1,per2,...,pern,请找到一个长度为n的小写字符串S,使得S能对应上per。输出字典序最小的那一个。

思路:

KMP算法中的一个性质: i − F a i l [ i ] i-Fail[i] iFail[i]表示以i结尾的子串的最短循环节长度。所以 f a i l [ i ] = i − p e r i fail[i]=i-per_i fail[i]=iperi 如果当前位置要推翻之前的循环节,即 f a i l [ i + 1 ] = 0 fail[i+1]=0 fail[i+1]=0,那么就沿着 f a i l fail fail指针往回跳,往回跳时遇到的所有字符都不能用,由于要输出字典序最小的那个,就贪心地选没有遇到的字典序最小的字符。 若 f a i l [ i + 1 ] ! = 0 fail[i+1]!=0 fail[i+1]!=0,那么当前字符就是 f a i l [ i + 1 ] fail[i+1] fail[i+1]那个位置的字符。

#include<cstdio> #include<cstring> #include<algorithm> using namespace std; #define MAXN 100010 int fail[MAXN],sta[MAXN],n; char ans[MAXN]; int main() { scanf("%d",&n); fail[0]=-1; for(int i=1,x;i<=n;i++) { scanf("%d",&x); fail[i]=i-x; } for(int i=0;i<n;i++) if(fail[i+1]) { ans[i]=ans[fail[i+1]-1]; int c=ans[i]-'a'; sta[i]=sta[fail[i]]|(1<<c); } else { int s,c; if(fail[i]==-1) s=0; else s=sta[fail[i]]; for(c=0;s&(1<<c);c++); ans[i]=c+'a'; sta[i]=sta[fail[i]]|(1<<c); } printf("%s\n",ans); }
转载请注明原文地址: https://www.6miu.com/read-5035352.html

最新回复(0)