正题
中等难度,大概想了10分钟就会做了
因为PAM可以管理所有本质不同的串,所以对于我们只需要考虑要多少步可以达到当前点的对应串,最后拿所有节点更新一下ans即可,具体更新就是n-len[x]+ans[x],ans[x]就是要多少步可以达到当前点的对应串.
因为翻转一下只能变成偶串,所以我们先考虑偶串:
对于一个偶串now的一个不超过长度/2后缀的后缀p,它对该串的贡献显然是,考虑先补齐一半再翻转一下.相当于取
中最小的,当长度增加时,len的减少速率肯定不比ans的增加速率慢,因为长度+1步数至多+1,所以我们只需要取最长的那个串.维护方法跟这题相同,不再赘述.
但是ans也不一定是由这样的后缀来贡献的,还可能是通过转移边来贡献的,(至于为什么可以考虑一个子串要翻转变为当前偶数串,可以在前面加东西,也就是通过后缀来转移,也可以在后面加东西,也就是通过转移边来转移).因为偶根没有计算翻转的贡献,所以偶根的贡献为2,否则贡献就是ans[p]+1.
#include<bits/stdc++.h>
using namespace std;
const int N=10000010;
struct Palindrome_AutoMaton{
int tot,n,T,las;
int ch[N][26],fail[N],num[N],len[N],ans[N];
char s[N];
void g0(int x){ch[x][0]=ch[x][19]=ch[x][2]=ch[x][6]=0;}
void init(){
tot=1;las=0;fail[0]=1;fail[1]=0;len[1]=-1;
g0(0);g0(1);
}
int gfail(int x,int pos){
while(s[pos-len[x]-1]!=s[pos]) x=fail[x];
return x;
}
void insert(int pos,int c){
int p=gfail(las,pos);
if(!ch[p][c]){
int now=++tot;g0(now);
fail[now]=ch[gfail(fail[p],pos)][c];
len[now]=len[p]+2;
int tmp=num[p];
while(s[pos-len[tmp]-1]!=s[pos] || len[ch[tmp][c]]*2>len[now]) tmp=fail[tmp];
num[now]=ch[tmp][c];ch[p][c]=now;
if(len[now]%2==0) ans[now]=min(len[now]/2-len[num[now]]+ans[num[now]]+1,p==0?2:ans[p]+1);
else ans[now]=len[now]-len[fail[now]]+ans[fail[now]];
}
las=ch[p][c];
}
void build(){
scanf("%d",&T);
while(T--){
scanf("%s",s+1);init();n=strlen(s+1);
for(int i=1;i<=n;i++) insert(i,s[i]-'A');
int mmin=1e9;
for(int i=2;i<=tot;i++) mmin=min(mmin,n-len[i]+ans[i]);
printf("%d\n",mmin);
}
}
}PAM;
int main(){
PAM.build();
}
本文深入探讨了PAM算法在处理字符串匹配和分析中的应用,特别是如何利用PAM管理不同字符串,计算达到特定状态所需的步骤。文章详细解释了算法的实现过程,包括如何处理偶数长度的字符串以及如何通过后缀和转移边贡献答案。
305

被折叠的 条评论
为什么被折叠?



