题目链接
https://vjudge.net/contest/248767#problem/C
题目大意
给你n个仅含’T’, ‘H’的串, 前n-1个串为已知的模式串, 第n个串为生成串, 生成串是根据n个模式串生成出来的。 对于生成串的第i位表示, 前缀i-1一共出现了偶数次模式串则为’T’, 奇数次则为’H’。 求缺失的模式串的方案数, 无穷则输出-1, 不能和已有的模式串重复。 (总串长≤106,任意两个串都不相同) ( 总 串 长 ≤ 10 6 , 任 意 两 个 串 都 不 相 同 )
题目思路
首先建立AC自动机, 将已有串的贡献消去, 剩下确实串对答案的影响, 即对于前缀i, 缺失串出现次数的奇偶性。
做差分数组, 表示缺失串是否为原串的前缀i的一个后缀
做翻转, 表示缺失串是否为原串的后缀i的一个前缀
如果所有元素都是0, 有无穷种。
否则仍取一个1, 对于其他的后缀, 求出lcp, 如果这个后缀对应元素为0, 则用lcp更新下界, 如果对应元素为1, 则更新上界。
最后将所有的可能用hash与已知模式串去重, 输出最后的方案数。
Code
#include <cstdio>
#include <iostream>
#include <algorithm>
#include <cmath>
#include <cstring>
#include <map>
#define ll long long
#define ull unsigned ll
using namespace std;
const int N = (int)1e6 + 10;
int n; char str[N]; int st[N], ed[N];
int rt, cnt, ch[N][2], fa[N], fail[N], g[N], pre[N];
int NewNd(){
cnt ++;
ch[cnt][0] = ch[cnt][1] = 0;
fa[cnt] = fail[cnt] = g[cnt] = pre[cnt] = 0;
return cnt;
}
int head, tail, que[N];
void make_fail(){
head = tail = 0;
que[tail = 1] = rt;
while (head < tail){
int u = que[++ head];
for (int j = 0; j < 2; j ++)
if (ch[u][j]) que[++ tail] = ch[u][j];
int p = fa[u];
while (u != rt && p != rt && !ch[fail[p]][pre[u]]) p = fail[p];
if (p == rt) continue;
fail[u] = ch[fail[p]][pre[u]];
g[u] += g[fail[u]];
}
}
int a[N], b[N], len; ull pw[N], ps[N];
ull get(int l, int r){
return ps[r] - ps[l - 1] * pw[r - l + 1];
}
int lcp(int x, int y){
int L = 1, R = min(len - y + 1, len - x + 1), ret = 0;
while (L <= R){
int mid = (L + R) / 2;
if (get(x, x + mid - 1) == get(y, y + mid - 1)) ret = mid, L = mid + 1;
else R = mid - 1;
}
return ret;
}
int main()
{
pw[0] = 1;
for (int i = 1; i < N; i ++) pw[i] = pw[i - 1] * 233;
while (scanf("%d", &n) != EOF){
ed[0] = 0;
for (int i = 1; i <= n; i ++){
st[i] = ed[i - 1] + 1;
scanf("%s", str + st[i]);
ed[i] = strlen(str + st[i]) + st[i] - 1;
}
for (int i = 1; i <= ed[n]; i ++)
str[i] = str[i] == 'T' ? '0' : '1';
cnt = 0; ch[0][0] = ch[0][1] = 0;
for (int i = 1; i < n; i ++){
int p = rt;
for (int j = st[i]; j <= ed[i]; j ++){
int nxt = str[j] - '0';
if (ch[p][nxt] == 0){
int Nd = NewNd();
ch[p][nxt] = Nd, fa[Nd] = p, pre[Nd] = nxt;
}
p = ch[p][nxt];
}
g[p] ++;
}
make_fail();
int sum = 0;
for (int j = st[n], u = rt; j < ed[n]; j ++){
int nxt = str[j] - '0';
while (u != rt && !ch[u][nxt]) u = fail[u];
if (!ch[u][nxt]) ;
else {u = ch[u][nxt]; sum += g[u];}
b[j - st[n] + 1] = abs(str[j + 1] - '0' - (sum & 1));
a[j - st[n] + 1] = nxt;
}
if (str[st[n]] != '0'){puts("0"); continue;}
len = ed[n] - st[n];
for (int j = len; j >= 1; j --) b[j] = abs(b[j] - b[j - 1]);
bool infi = 1;
for (int j = 1; j <= len && infi; j ++)
if (b[j] != 0) infi = 0;
if (infi) {puts("-1"); continue;}
reverse(a + 1, a + len + 1);
reverse(b + 1, b + len + 1);
int x = 0, ans = 0;
for (int i = 1; i <= len && !x; i ++)
if (b[i]) x = i;
map<ull, bool > M;
for (int i = 1; i < n; i ++){
ull tmp = 0;
for (int j = ed[i]; j >= st[i]; j --)
tmp = tmp * 233 + str[j] - '0' + 1;
M[tmp] = 1;
}
for (int i = 1; i <= len; i ++) ps[i] = ps[i - 1] * 233 + a[i] + 1;
int L = 1, R = len - x + 1;
for (int i = 1; i <= len; i ++){
if (i == x) continue;
int res = lcp(i, x);
if (b[i] == 0) L = max(L, res + 1);
else R = min(R, res);
}
for (int i = L; i <= R; i ++){
ull ret = get(x, x + i - 1);
if (!M[ret]) ans ++;
}
printf("%d\n", ans);
}
return 0;
}