分析
题目要求原字符串可以分解成多少种 ( A B ) i C (AB)^iC (AB)iC的形式,该式子的意义见题面。
如果不考虑那个奇怪的奇偶限制,那么应该怎么做?
首先有两种想法,肯定是先枚举一部分,然后通过一些奇怪的优化来解决。
比较方便的做法,就是枚举
A
B
AB
AB,这样可以在之后找循环节来解决,然后确定
C
C
C,而接下来的问题就是我需要知道循环节。
于是正解用
e
x
K
M
P
exKMP
exKMP,于是……
大炮打蚊子—xinjun
于是参照了字符串的题目,想到哈希。
我可以预处理出到每个位置的哈希值,然后用前缀和的方式将它求出每一段的哈希值,可以
O
(
1
)
O(1)
O(1)比较两段字符串是否相等。可以快速地找循环节。
比如我当前枚举的
A
B
AB
AB,那么在
A
B
AB
AB后面的等长的哈希值判一判就可以了。
解决上述问题后,可以来看奇偶的限制了。
首先很简单可以得出:
A
A
A是整个串的前缀,
C
C
C是整个串的后缀,可以预处理出每个位置前后缀的奇偶,然后进行判断。
看似好像解决了吼,但是!会遇到这种情况:有多个循环节,但是我可以把最后一个循环节算成
C
C
C,这似乎也是可以的。如果不考虑奇偶,就是循环节的个数,但是考虑了奇偶之后就显得麻烦了。(这里添一嘴,我一开始想过为什么不要考虑把循环节合并后又是几个循环节的情况,但是我枚举的是循环节的长度,两个或者几个合并就不在当前的枚举范围内,之后会算进去的)
那么我们不妨将之情形展示一下,比如,我当前判断
(
A
B
)
i
C
(AB)^iC
(AB)iC合法,奇偶也合法,那么
(
A
B
)
i
−
1
A
B
C
(AB)^{i-1}ABC
(AB)i−1ABC是否合法呢?不知道,因为
B
B
B的奇偶性我不知道,之前的判定是依靠前缀的奇偶来解决
A
A
A的奇偶,如果再判
B
B
B的显然会超时1,那怎么办?
别急,继续拆一个,
(
A
B
)
i
−
2
A
B
A
B
C
(AB)^{i-2}ABABC
(AB)i−2ABABC是否合法?合法,并且一定合法2。为什么?因为
A
B
A
B
ABAB
ABAB一定每个字母都出现偶数次,对后缀的奇偶不产生影响。
所以定义函数
o
d
l
(
S
)
odl(S)
odl(S)表示
S
S
S的奇偶性,则
o
d
l
(
S
)
=
o
d
l
(
A
A
S
)
odl(S)=odl(AAS)
odl(S)=odl(AAS)。
所以只要判断后缀就可以推广至全部了。
最后一个问题,枚举了
A
B
AB
AB后,无法确定
A
,
B
A,B
A,B的分割点。确切地说,枚举了一个串
S
S
S,但是这个
S
S
S可以有很多种方式拆成
A
A
A和
B
B
B的形式,因此我们需要枚举
A
A
A,然后判断是否合法。
结果超时了。那么结果怎么办?诶发现字母是只有26个的,我可以存一下到目前为止,每个出现奇数次字母个数相等的前缀个数,即用
t
[
i
]
t[i]
t[i]表示前几个前缀中有多少个出现奇数次字母个数是
i
i
i个的。然后对于每个后缀,都枚举一遍比它小的个数,直接加在
a
n
s
ans
ans中即可。
代码
P
S
:
本
代
码
可
能
存
在
姿
势
不
好
等
问
题
,
O
J
不
稳
定
的
时
候
可
能
会
T
,
\color{Red}{PS:本代码可能存在姿势不好等问题,OJ不稳定的时候可能会T,}
PS:本代码可能存在姿势不好等问题,OJ不稳定的时候可能会T,
希
望
各
位
自
己
写
,
仅
仅
提
供
参
考
。
\,\,\quad\quad\color{Red}{希望各位自己写,仅仅提供参考。}
希望各位自己写,仅仅提供参考。
或者趁OJ不注意,卡过去,我就是这么在luogu上过的233,以后发誓再(yi)也(ding)不(yao)卡评测。
#include<bits/stdc++.h>
#define ull unsigned long long
#define ll long long
using namespace std;
const int MAXN=(1<<20)+100;
const int B=499;
char s[MAXN];
ull h[MAXN],base[MAXN];//ull自动溢出
ull geth(int l,int r){return h[r]-h[l-1];}
int odp[MAXN],odl[MAXN],cnt[30],t[30];
//odp是前缀奇数个数odd_pre
//同理odd_last
int main()
{
// freopen("string.in","r",stdin);
// freopen("string.out","w",stdout);
int T,len;
scanf("%d",&T);
while(T--){
scanf("%s",s+1);
len=strlen(s+1);
base[1]=1;h[0]=0;
for(int i=1;i<=len;i++){
h[i]=base[i]*s[i]+h[i-1];
base[i+1]=base[i]*B;
}
memset(cnt,0,sizeof(cnt));
odp[0]=0;
for(int i=1;i<=len;i++){
odp[i]=odp[i-1];
if(cnt[s[i]-'a']&1) odp[i]--;
else odp[i]++;
cnt[s[i]-'a']++;
}
memset(cnt,0,sizeof(cnt));
odl[len+1]=0;
for(int i=len;i>=1;i--){
odl[i]=odl[i+1];
if(cnt[s[i]-'a']&1) odl[i]--;
else odl[i]++;
cnt[s[i]-'a']++;
}
memset(t,0,sizeof(t));
ll ans=0;t[1]=1;
for(int x=2;x<len;x++){
int tm=0,st=0;
ull cur=geth(1,x);
while(geth(st+1,st+x)==cur*base[st+1]&&st+x<len) tm++,st+=x;//不断向后找
st-=x;
for(int i=odl[st+x+1];i>=0;i--) ans+=((tm+1)/2)*t[i];
for(int i=odl[st+1];i>=0;i--) ans+=(tm/2)*t[i];
t[odp[x]]++;
}printf("%lld\n",ans);
}
}
END
大家可以去你谷上看看卡常技巧,毕竟不是正解。
不过这题这样的思维是真心不错!!