题意
给一个串,划分成k个部分,使得这k个部分整体是回文
题解
转化为
s
1
s
n
s
2
s
n
−
1
.
.
.
s
n
/
2
s
n
/
2
+
1
s_1s_ns_2s_{n-1}...s_{n/2}s_{n/2+1}
s1sns2sn−1...sn/2sn/2+1
变为划分为k个部分,每个部分内部是回文
d
p
dp
dp 方程很好想,
d
p
[
i
]
=
∑
d
p
[
j
]
∗
[
s
[
j
+
1
,
i
]
为
回
文
]
dp[i]=\sum dp[j]*[s[j+1,i]为回文]
dp[i]=∑dp[j]∗[s[j+1,i]为回文]
朴素的做法是暴力跳
f
a
i
l
fail
fail,TLE,考虑优化
一个回文串的所有的后缀回文串可以组成不超过
l
o
g
log
log 个等差数列,所以,整个等差数列一起更新
设一个等差数列为
a
i
a
i
−
1
a
i
−
2
.
.
.
a
1
(
l
e
n
[
a
i
]
>
l
e
n
[
a
i
−
1
]
)
a_ia_{i-1}a_{i-2}...a_1(len[a_i]>len[a_{i-1} ])
aiai−1ai−2...a1(len[ai]>len[ai−1])
g
[
i
]
g[i]
g[i] 表示从
i
到
1
i到1
i到1 的和,即
g
[
i
]
=
f
[
p
o
s
−
a
i
]
+
f
[
p
o
s
−
a
i
−
1
]
+
.
.
.
+
f
[
p
o
s
−
a
1
]
g[i]=f[pos-a_{i}]+f[pos-a_{i-1}]+...+f[pos-a_{1}]
g[i]=f[pos−ai]+f[pos−ai−1]+...+f[pos−a1]
那么,
f
[
p
o
s
]
f[pos]
f[pos] 就等于所有的等差数列的首相对应的
g
[
i
]
g[i]
g[i] 的和
下面考虑如何维护
g
[
i
]
g[i]
g[i]
已知
g
[
3
]
g[3]
g[3],考虑如何得出
g
[
4
]
g[4]
g[4] ,发现
g
[
4
]
=
f
[
k
−
3
′
]
+
f
[
k
−
2
′
]
+
f
[
k
−
1
′
]
+
f
[
k
−
1
′
′
]
~~~g[4]=f[k-3']+f[k-2']+f[k-1']+f[k-1'']
g[4]=f[k−3′]+f[k−2′]+f[k−1′]+f[k−1′′]
而
g
[
3
]
=
f
[
k
−
3
]
+
f
[
k
−
2
]
+
f
[
k
−
1
]
=
f
[
k
−
3
′
]
+
f
[
k
−
2
′
]
+
f
[
k
−
1
′
]
g[3]=f[k-3]+f[k-2]+f[k-1]\\~~~~~~~~~~~=f[k-3']+f[k-2']+f[k-1']
g[3]=f[k−3]+f[k−2]+f[k−1] =f[k−3′]+f[k−2′]+f[k−1′]
其中,根据 border 理论(这个不明白的话自己学一下),
1
′
′
和
1
′
1''和1'
1′′和1′ 是完全相同的
所以,
g
[
4
]
g[4]
g[4]为
g
[
3
]
g[3]
g[3] 的基础上,再加上
f
[
k
−
1
′
′
]
f[k-1'']
f[k−1′′] 即可
进一步,可以发现,其实就是每次加上数列的末项!!!
用
u
p
[
j
]
up[j]
up[j] 表示结点
j
j
j 所在数列的末项
f
[
k
−
l
e
n
[
u
p
[
j
]
]
f[k-len[up[j]]
f[k−len[up[j]] 就是
f
[
k
−
1
′
′
]
f[k-1'']
f[k−1′′]
然后在加上
g
[
f
a
i
l
[
j
]
]
g[fail[j]]
g[fail[j]] 就是更新后的
g
[
j
]
g[j]
g[j] 了,累加到
f
[
k
]
f[k]
f[k] 即可
吐槽:
为什么别人的博客写的那么晦涩,最关键的
g
[
j
]
g[j]
g[j]的更新根本看不懂
全部写成
f
[
k
−
(
l
e
n
[
f
a
i
l
[
u
p
[
j
]
]
+
d
i
f
f
[
j
]
)
]
f[k-(~len[fail[up[j]~]+diff[j]~)~]
f[k−( len[fail[up[j] ]+diff[j] ) ] 根本就是个坑
算了,还是自己太弱了
代码
#include<bits/stdc++.h>
#define N 1000010
#define INF 0x3f3f3f3f
#define eps 1e-6
#define pi 3.141592653589793
#define mod 1000000007
#define P 1000000007
#define LL long long
#define pb push_back
#define fi first
#define se second
#define cl clear
#define si size
#define lb lower_bound
#define ub upper_bound
#define bug(x) cerr<<#x<<" : "<<x<<endl
#define mem(x,y) memset(x,0,sizeof(int)*(y+3))
#define sc(x) scanf("%d",&x)
#define scc(x,y) scanf("%d%d",&x,&y)
#define sccc(x,y,z) scanf("%d%d%d",&x,&y,&z)
using namespace std;
typedef pair<int,int> pp;
LL ans=0;
LL f[N],g[N];
struct PAM {
int next[N][26] ;//next指针,next指针和字典树类似,指向的串为当前串两端加上同一个字符构成
int fail[N] ;//fail指针,失配后跳转到fail指针指向的节点
int len[N] ;//len[i]表示节点i表示的回文串的长度
int s[N] ;//存放添加的字符
int last ;//指向上一个字符所在的节点,方便下一次add
int n ;//字符数组指针
int p ;//节点指针
int d[N],up[N],id[N];
inline int newnode ( int l ) {//新建节点
for ( int i = 0 ; i < 26 ; ++ i ) next[p][i] = 0 ;
len[p] = l ;
return p ++ ;
}
inline void init () {//初始化
p = 0 ;
newnode ( 0 ) ;
newnode ( -1 ) ;
last = 0 ;
n = 0 ;
s[n] = -1 ;//开头放一个字符集中没有的字符,减少特判
fail[0] = 1 ;
}
inline int get_fail ( int x ) {//和KMP一样,失配后找一个尽量最长的
while ( s[n - len[x] - 1] != s[n] ) x = fail[x] ;
return x ;
}
inline void add ( int c,int w ) {
c -= 'a' ;
s[++ n] = c ;
int cur = get_fail ( last ) ;//通过上一个回文串找这个回文串的匹配位置
if ( !next[cur][c] ) {//如果这个回文串没有出现过,说明出现了一个新的本质不同的回文串
int now = newnode ( len[cur] + 2 ) ;//新建节点
fail[now] = next[get_fail ( fail[cur] )][c] ;
next[cur][c] = now ;
d[now]=len[now]-len[fail[now]];
up[now]=(d[fail[now]]==d[now]?up[fail[now]]:now);
}
last = next[cur][c] ;
id[w]=last;
}
void slove(){
for(int i=1;i<=n;i++)
for(int j=id[i];j;j=fail[up[j]]){//遍历所有i为结尾的回文串,按照等差整块遍历,最多log次
g[j]=f[i-len[up[j]]];
if (d[j]==d[fail[j]]) g[j]=(g[j]+g[fail[j]])%mod;
if (!(i&1)) f[i]=(f[i]+g[j])%mod;
}
}
}A;
char s[N];
char ss[N];
int main(int argc, char const *argv[]){
scanf("%s",s+1);
int n=strlen(s+1);
for(int i=1;i<=n;i+=2) ss[i]=s[i/2+1];
for(int i=2;i<=n;i+=2) ss[i]=s[n-i/2+1];
A.init();
for(int i=1;i<=n;i++) A.add(ss[i],i);
f[0]=1;
A.slove();
printf("%lld\n",f[n]);
return 0;
}