[AtCoder] AT4379 [AGC027E] ABBreviate
Description
Solution
设\(G(S)=\sum\limits_{i=1}^{|S|}(S_i-'a'+1)\pmod{3}\)。那么经过若干次变换,\(G(S)\)始终不会改变。
注意到一个字符串\(S\)能变换成\(T\)(\(S\neq{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_{i\sim{n}})\)。
解释:可以由\(dp[nxt[i][0]]\)再向前面添一个\(a\)得来,也可以由\(dp[nxt[i][1]]\)再向前面添一个\(b\)得来,还可以直接作为一个后缀(这要满足\(G(S_{i\sim{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\rightarrow{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\n", (dp[1] - (s == 0) + mod) % mod);
return 0;
}