初涉manacher

一直没有打过……那么今天来找几道题打一打吧

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\n",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都是回文串。

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\n",ans);
26     return 0;
27 }

bzoj3790: 神奇项链

Description

母亲节就要到了,小 H 准备送给她一个特殊的项链。这个项链可以看作一个用小写字
母组成的字符串,每个小写字母表示一种颜色。为了制作这个项链,小 H 购买了两个机器。第一个机器可以生成所有形式的回文串,第二个机器可以把两个回文串连接起来,而且第二个机器还有一个特殊的性质:假如一个字符串的后缀和一个字符串的前缀是完全相同的,那么可以将这个重复部分重叠。例如:aba和aca连接起来,可以生成串abaaca或 abaca。现在给出目标项链的样式,询问你需要使用第二个机器多少次才能生成这个特殊的项链。 

Input

输入数据有多行,每行一个字符串,表示目标项链的样式。 

Output

多行,每行一个答案表示最少需要使用第二个机器的次数。 

Sample Input

abcdcba
abacada
abcdef

Sample Output

0
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\n",ans);
36     }
37     return 0;
38 }

 

 

END

posted @ 2018-10-20 09:56  AntiQuality  阅读(150)  评论(0编辑  收藏  举报