本题我是想到了个做法的,成功的过了第一个样例,后来与题解比对,发现我的做法考察的条件还是太弱了(
容易发现,最后的每个字符都是由原串的一段区间操作得来的。
把 A,B,C 的权值分别看成 ,那么一次操作相当于替换为异或和。
考虑选出一段区间能干嘛:
- 如果这段区间的异或和为 ,显然不能操作为单个字符。
- 如果这段区间只有一种字符,显然啥都干不了。
- 除开上述两种情况,归纳可得这段区间肯定能被操作成单个字符。我们称这类区间为
“好段”
(我之前的做法默认区间长度 ,理所当然的寄了)
然后就是利用常规的思维模式,考虑如何让原串 唯一映射到能被构造出来的串 。
首先 的异或和要等于 的异或和。
其次,我们在找方案的时候,只考虑每个“好段”右端点最靠前的方案。
有一个转移方法是,每次选一个最短的可行的段向后转移,如果
的最后一个字符取完了但还有剩余,就把剩下的字符全部补在后面(显然这些字符异或和肯定为
)。
为了说明正确性,我们要说明对于可以被构造的串 ,上述方法一定能够构造一个方案。
我们取出串
的任意一个方案,去掉第一个“好段”的最长的异或和
的后缀,且保证去掉后第一段仍是“好段”,把这段后缀接在第二个”好段“的前方,以此类推,这样得到的方案就能被上面的转移方法所表示。
那么我们设 表示处理了前
个字符的方案数,预处理 表示 位置往后最短的异或和为 的“好段”,据此转移即可。
最后取出 的异或和为
的位置 ,将答案加上 即可。
注意整个串清一色要特判,这个时候补 操作是错的,但答案为 。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39
| #include <bits/stdc++.h> using namespace std; const int N = 1e6 + 5,P = 1e9 + 7; int n; char s[N]; int f[N]; int ton[4],nxt[N][4]; int sum[N]; int main() { cin >> n >> (s + 1); int flg = 1; for(int i = 1;i <= n;i++) { if(i < n) flg &= (s[i] == s[i + 1]); sum[i] = sum[i - 1] ^ (s[i] == 'A' ? 1 : (s[i] == 'B' ? 2 : 3)); } if(flg) return puts("1"),0; for(int i = 1;i <= 3;i++) ton[i] = n + 1; for(int i = n;i >= 0;i--) { ton[sum[i]] = i; for(int k = 0;k <= 3;k++) if(k != sum[i]) nxt[i][sum[i] ^ k] = ton[k]; nxt[i][sum[i] ^ sum[i + 1]] = i + 1; } f[0] = 1; for(int i = 0;i < n;i++) for(int k = 1;k <= 3;k++) (f[nxt[i][k]] += f[i]) %= P; int ans = 0; for(int i = 1;i <= n;i++) if(sum[i] == sum[n]) (ans += f[i]) %= P; cout << ans << endl; return 0; }
|