noi.ac 34 洛谷 4656 libreoj 2484 [CEOI2017]Palindromic Partitions 题解

(noi.ac题面被改过了,题意是一样的)

题意简述

给你一个字符串$S$,长度$1e6$。
$S$的一个$k$个串的划分$a_1,a_2…a_k$,满足:对于任意的$i$,$a_i=a_{k-i+1}$,这就是$S$的一个“回文划分”,它被分成了$k$块。
请你求$S$中被分成的块数最多的一个回文划分,输出这个最多的块数。

比如样例中的“bonobo”这个串,最大的划分就是“bo/no/bo”,3块。

思路框架

从两边往中间,每次找最短的串,使得它在前面和后面同时出现(哈希判断)。

到最后,如果$n$为奇数,或者中间一块不能再分了,就把答案$+1$(中间一块单独划分出来)。

代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72

#include <bits/stdc++.h>using namespace std;
namespace Flandre_Scarlet
{
#define N 1666666
#define hint unsigned long long //自然溢出的哈希
#define F(i,l,r) for(int i=l;i<=r;++i)
#define D(i,r,l) for(int i=r;i>=l;--i)
#define Fs(i,l,r,c) for(int i=l;i<=r;c)
#define Ds(i,r,l,c) for(int i=r;i>=l;c)
#define MEM(x,a) memset(x,a,sizeof(x))
#define FK(x) MEM(x,0)
#define Tra(i,u) for(int i=G.Start(u),__v=G.To(i);~i;i=G.Next(i),__v=G.To(i))
#define p_b push_back
#define sz(a) ((int)a.size())
#define iter(a,p) (a.begin()+p)
void R1(int &x)
{
x=0;char c=getchar();int f=1;
while(c<'0' or c>'9') f=(c=='-')?-1:1,c=getchar();
while(c>='0' and c<='9') x=(x<<1)+(x<<3)+(c^48),c=getchar();
x=(f==1)?x:-x;
}
void Rd(int cnt,...)
{
va_list args;
va_start(args,cnt);
F(i,1,cnt)
{
int* x=va_arg(args,int*);R1(*x);
}
va_end(args);
}

char a[N]; int n;
void Input()
{
scanf("%s",a+1); n=strlen(a+1);
}

void Soviet()
{
hint base=79;
hint s1=0,s2=0,b=1;
int ans=0;
F(i,1,n/2)
{
s1=s1*base+a[i]; //在s1后面加入a[i]
s2=s2+a[n-i+1]*b; //在s2的前面加入a[i]
b*=base;
if (s1==s2) {ans+=2;s1=s2=0,b=1;} //找到第一个满足条件就记录答案
}
if (n%2 or s1) ++ans;
printf("%d\n",ans);
}

#define Flan void
Flan IsMyWife()
{
int t;R1(t);
while(t--)
{
Input();
Soviet();
}
}
}
int main(){
Flandre_Scarlet::IsMyWife();
getchar();getchar();
return 0;
}
w