Description
Solution
设(G(S)=sumlimits_{i=1}^{|S|}(S_i-'a'+1)pmod{3})。那么经过若干次变换,(G(S))始终不会改变。
注意到一个字符串(S)能变换成(T)((S eq{T})),当且仅当(S)能被划分成若干段,满足每一段的(G(S'))都等于(G(T_i))且(S)中至少有两个连续字符相同,且若还剩下最后一段没有匹配,则(G(S_{lst})=0)。(因为(G(S)=G(T)))
举个例子:设(S=aabbabaabaaabab),(T=abba),那么(S)会被划分为(a|abb|abaa|baa|abab)。这是满足的,因为(G(abab)=0)。
因此,我们设(dp[i])表示在(S)中,以(S_i)为后缀的答案。
最开始先把形如(ababababababa)的判掉。
再设一个(nxt[i][0/1])表示(dp[i])由哪里更新而来(如由(dp[nxt[i][0]])再向前面添一个(a)得来)
那么转移方程即为(dp[i] = dp[nxt[i][0]] + dp[nxt[i][1]] + (s == 0)),其中(s)表示(G(S_{isim{n}}))。
解释:可以由(dp[nxt[i][0]])再向前面添一个(a)得来,也可以由(dp[nxt[i][1]])再向前面添一个(b)得来,还可以直接作为一个后缀(这要满足(G(S_{isim{n}})=0))
(nxt)数组的更新如下:
以(nxt[i][0])为例:如果(S_i=a),那么(nxt[i][0]=i+1);如果(S_i=S_{i+1}=b),那么(nxt[i][0]=i+2);((bb ightarrow{a})),否则(nxt[i][0]=nxt[i+2][0])。例如,在(S=babba,i=1)时,计算可得(nxt[i][0]=5)。发现(babb)恰好可以转化为(a)。
答案为(dp[1])。当然,要记得最后减掉(s==0),因为整个串不可能单独做一个后缀。
Code
#include <bits/stdc++.h>
using namespace std;
const int mod = 1e9 + 7;
int n, s, dp[100005], nxt[100005][2];
char ch[100005];
int read()
{
int x = 0, fl = 1; char ch = getchar();
while (ch < '0' || ch > '9') { if (ch == '-') fl = -1; ch = getchar();}
while (ch >= '0' && ch <= '9') {x = (x << 1) + (x << 3) + ch - '0'; ch = getchar();}
return x * fl;
}
int main()
{
scanf("%s", ch + 1);
n = strlen(ch + 1);
int ok = 0;
for (int i = 1; i <= n - 1; i ++ )
if (ch[i] == ch[i + 1])
ok = 1;
if (!ok)
{
puts("1");
return 0;
}
dp[n + 1] = 1;
nxt[n + 1][0] = nxt[n + 1][1] = n + 2;
nxt[n + 2][0] = nxt[n + 2][1] = n + 2;
for (int i = n; i >= 1; i -- )
{
s = (s + ((ch[i] == 'a') ? 1 : 2)) % 3;
if (ch[i] == 'a') nxt[i][0] = i + 1;
else if (ch[i + 1] == 'b') nxt[i][0] = i + 2;
else nxt[i][0] = nxt[i + 2][0];
if (ch[i] == 'b') nxt[i][1] = i + 1;
else if (ch[i + 1] == 'a') nxt[i][1] = i + 2;
else nxt[i][1] = nxt[i + 2][1];
dp[i] = (dp[nxt[i][0]] + dp[nxt[i][1]] + (s == 0)) % mod;
}
printf("%d
", (dp[1] - (s == 0) + mod) % mod);
return 0;
}