先考虑如何判定能否将字符串缩为单个字符。考虑将
a
a
a当做
1
1
1,
b
b
b当做
2
2
2,这样每次操作后,总和在
m
o
d
3
\bmod \ 3
mod 3意义下不变,那么有解需要和在
m
o
d
3
\bmod 3
mod3意义下不为
0
0
0。并且若串长
>
1
>1
>1,需要有两个相邻的相同字符,容易归纳证明这个条件也是充要的,当存在两种字符时,每次操作一个
a
b
b
abb
abb类似的即可。
再考虑如何计数。我们发现匹配某个字符串的时候大致是贪心的,也即从右往左考虑模式串每个字符,尽可能的取原串最右边最短部分匹配。于是可以dp处理。但有一种特殊情况,考虑串
a
b
a
b
b
ababb
ababb和
a
a
aa
aa匹配,如果贪心匹配,会用右边的
b
b
bb
bb和
a
a
a匹配,此时左边的
a
b
a
aba
aba匹配不了
a
a
a,唯一合法选择是用右边的
b
a
b
b
babb
babb和
a
a
a匹配。这种情况发生只可能出现在原串最左边的
a
b
a
b
a
.
.
.
ababa...
ababa...或
b
a
b
a
b
.
.
.
babab...
babab...后面接的一个段,特殊处理即可。
时间复杂度
O
(
∣
s
∣
)
\mathcal O(|s|)
O(∣s∣)。
#include <bits/stdc++.h>
#define MOD 1000000007
#define last last2
using namespace std;
char str[100005];
int f[100005],last[100005][3];
int main() {
memset(last,255,sizeof(last));
scanf("%s",str+1);
int n=strlen(str+1);
int nr=0;
while (nr<n&&str[nr+1]!=str[nr]) nr++;
int l=0,s=0;
last[0][0]=0;
for(int i=1;i<=n;i++) {
s=(s+str[i]-'a'+1)%3;
memcpy(last[i],last[i-1],sizeof(last[i]));
last[i][s]=i;
if (str[i]==str[i-1]) l=i-2;
int u=last[l][(s+2)%3],v=last[l][(s+1)%3];
if (str[i]=='a') u=i-1; else v=i-1;
if (u!=-1) {
f[i]=(f[i]+f[u])%MOD;
if (u&&nr>=u&&nr<i) f[i]=(f[i]+((u-1)>>1))%MOD;
}
if (v!=-1) {
f[i]=(f[i]+f[v])%MOD;
if (v&&nr>=v&&nr<i) f[i]=(f[i]+((v-1)>>1))%MOD;
}
if (s&&(i==1||nr<i)) f[i]=(f[i]+1)%MOD;
}
printf("%d\n",f[n]);
return 0;
}