先考虑如何判定能否将字符串缩为单个字符。考虑将 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
#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;
}