一直没有打过……那么今天来找几道题打一打吧
manacher有什么用
字符串的题有一类是专门关于“回文”的。通常来说,这类问题要么和一些dp结合在一起;要么是考察对于manacher(或其他如回文自动机)的理解。
裸的manacher则是解决形如“一个字符串内最长回文串长度”的问题。
一些例题
luoguP3805 【模板】manacher算法
题目描述
给出一个只由小写英文字符a,b,c...y,z组成的字符串S,求S中最长回文串的长度.
字符串长度为n
输入输出格式
输入格式:
一行小写英文字符a,b,c...y,z组成的字符串S
输出格式:
一个整数表示答案
输入输出样例
输入样例#1:
aaa
输出样例#1:
3
说明
字符串长度len <= 11000000
题目分析
注意f[maxn<<1]
1 #include<bits/stdc++.h> 2 const int maxn = 11000035; 3 4 int n,f[maxn<<1],mid,ans; 5 char s[maxn],t[maxn<<1]; 6 7 int main() 8 { 9 scanf("%s",s+1), t[0] = '!', t[++n] = '#'; 10 for (int i=1; s[i]; i++) t[++n] = s[i], t[++n] = '#'; 11 for (int i=1, mx=-1; i<n; i++) 12 { 13 if (i < mx) f[i] = std::min(f[(mid<<1)-i], mx-i); 14 else f[i] = 1; 15 for (; t[i+f[i]]==t[i-f[i]]; f[i]++); 16 if (i+f[i] > mx) mx = i+f[i], mid = i; 17 ans = f[i] > ans?f[i]:ans; 18 } 19 printf("%d ",ans-1); 20 return 0; 21 }
bzoj2565: 最长双回文串
Description
顺序和逆序读起来完全一样的串叫做回文串。比如acbca是回文串,而abc不是(abc的顺序为“abc”,逆序为“cba”,不相同)。
输入长度为n的串S,求S的最长双回文子串T,即可将T分为两部分X,Y,(|X|,|Y|≥1)且X和Y都是回文串。
输入长度为n的串S,求S的最长双回文子串T,即可将T分为两部分X,Y,(|X|,|Y|≥1)且X和Y都是回文串。
Input
一行由小写英文字母组成的字符串S。
Output
一行一个整数,表示最长双回文子串的长度。
Sample Input
baacaabbacabb
Sample Output
12
HINT
样例说明
从第二个字符开始的字符串aacaabbacabb可分为aacaa与bbacabb两部分,且两者都是回文串。
对于100%的数据,2≤|S|≤10^5
题目分析
枚举断点,并处理出每一个位置所能够作为起点扩展出的最远位置。
有一些挺好的细节:【BZOJ2565】最长双回文串 Manacher
1 #include<bits/stdc++.h> 2 const int maxn = 100035; 3 4 int n,ans,mid,f[maxn<<1],ls[maxn<<1],rs[maxn<<2]; 5 char s[maxn],t[maxn<<1]; 6 7 inline void Max(int &x, int y){x = x>y?x:y;} 8 int main() 9 { 10 scanf("%s",s+1), t[0] = '!', t[++n] = '#'; 11 for (int i=1; s[i]; i++) t[++n] = s[i], t[++n] = '#'; 12 for (int i=1, mx=-1; i<=n; i++) 13 { 14 if (i < mx) f[i] = std::min(f[(mid<<1)-i], mx-i); 15 else f[i] = 1; 16 for (; t[i-f[i]]==t[i+f[i]]; f[i]++); 17 if (i+f[i] > mx) mx = i+f[i], mid = i; 18 Max(rs[i-f[i]+1], f[i]-1); 19 Max(ls[i+f[i]-1], f[i]-1); 20 } 21 for (int i=3; i<=n; i+=2) Max(rs[i], rs[i-2]-2); 22 for (int i=n; i>=1; i-=2) Max(ls[i], ls[i+2]-2); 23 for (int i=1; i<=n; i+=2) 24 if (ls[i]&&rs[i]) ans = std::max(ans, ls[i]+rs[i]); 25 printf("%d ",ans); 26 return 0; 27 }
bzoj3790: 神奇项链
Description
母亲节就要到了,小 H 准备送给她一个特殊的项链。这个项链可以看作一个用小写字
母组成的字符串,每个小写字母表示一种颜色。为了制作这个项链,小 H 购买了两个机器。第一个机器可以生成所有形式的回文串,第二个机器可以把两个回文串连接起来,而且第二个机器还有一个特殊的性质:假如一个字符串的后缀和一个字符串的前缀是完全相同的,那么可以将这个重复部分重叠。例如:aba和aca连接起来,可以生成串abaaca或 abaca。现在给出目标项链的样式,询问你需要使用第二个机器多少次才能生成这个特殊的项链。
Input
输入数据有多行,每行一个字符串,表示目标项链的样式。
Output
多行,每行一个答案表示最少需要使用第二个机器的次数。
Sample Input
abcdcba
abacada
abcdef
abacada
abcdef
Sample Output
0
2
5
2
5
HINT
每个测试数据,输入不超过 5行
每行的字符串长度小于等于 50000
题目分析
沿用上一题的做法,manacher预处理出最远扩展到的位置。之后就是一个贪心的向远处跳的过程。
(好像预处理暴力也行?)
也挺妙的:【BZOJ3790】神奇项链 Manacher+贪心
1 #include<bits/stdc++.h> 2 const int maxn = 50003; 3 4 int n,ans,mid,now,nxt,f[maxn<<1],rs[maxn<<1]; 5 char s[maxn],t[maxn<<1]; 6 7 int main() 8 { 9 while (scanf("%s",s+1)!=EOF) 10 { 11 memset(f, 0, sizeof f); 12 ans = n = 0, t[0] = '!', t[++n] = '#'; 13 for (int i=1; s[i]; i++) t[++n] = s[i], t[++n] = '#'; 14 for (int i=1, mx=-1; i<n; i++) 15 { 16 if (i < mx) f[i] = std::min(f[(mid<<1)-i], mx-i); 17 else f[i] = 1; 18 for (; t[i+f[i]]==t[i-f[i]]; f[i]++); 19 if (i+f[i] > mx) mx = i+f[i]-1, mid = i; 20 rs[i-f[i]+1] = i+f[i]-1; 21 } 22 // now = 1, nxt = 0; 23 // while (rs[now] < n) 24 // { 25 // for (int i=now+1; i<=rs[now]; i++) 26 // if (rs[i] > rs[nxt]) nxt = i; 27 // ans++, now = nxt; 28 // } //这种是不是不行啊……? 29 now = nxt = rs[1]+2; 30 for (int i=1; i<=n; i+=2) 31 { 32 if (i==now) now = nxt, ans++; 33 nxt = std::max(nxt, rs[i]+2); 34 } 35 printf("%d ",ans); 36 } 37 return 0; 38 }
END