Victor and String
题意
维护字符串,可以执行以下操作(初始为空串)
- 前端插入一个字符
- 后端插入一个字符
- 查询不同本质回文串个数
- 查询所有回文串个数
思路
前端插入维护
-
pre,last
两个标记维护前后添加以
pre
为例,当添加节点后-
新增的节点后最长回文串不是本身时,新添加的节点不会影响
last
即 l e n [ l a s t ] , f a i l [ l a s t ] len[last],fail[last] len[last],fail[last]为正确的
-
新增的节点后,本身为最长回文串时,要更新
last
更新方法为将 l a s t = p r e last=pre last=pre
-
-
维护不同本质字符串个数和总共字符串个数是正确的
因为维护新增的节点为尾节点的回文串个数是不会重复的
- 不同本质字符串树即为 n u m − 1 num-1 num−1
- 字符串总数维护:每增加一个节点增加以该节点为尾节点的回文串数
代码
初始化
int stdl = base + 1, stdr = base;//初始串的开始位置,base=100005
void init() {
memset(in, 0, sizeof(in));
memset(mark, 0, sizeof(mark));
memset(tree, 0, sizeof(tree));
len[0] = 0; len[1] = -1;
fail[0] = 1; fail[1] = 0;
num = 1;
pre = last = 0; //前指针,后指针
sum = 0; //sum答案
}
前端插入
void pre_insert(char* in, int i) {
while (in[i + len[pre] + 1] != in[i]) pre = fail[pre];
//前端的节点对应节点为i+len[pre]+1,下同
if (!tree[pre][in[i] - 'a']) {
len[++num] = len[pre] + 2;
int j = fail[pre];
while (in[i + len[j] + 1] != in[i]) j = fail[j];
fail[num] = tree[j][in[i] - 'a'];
tree[pre][in[i] - 'a'] = num;
mark[num] = mark[fail[num]] + 1;//维护以该节点为为节点的回文串数
}
pre = tree[pre][in[i] - 'a'];
if (len[pre] == stdr - stdl + 1) last = pre;//若本身为回文串更新last
sum = sum + mark[pre]; //维护sum
}
后端插入
通常的回文自动机
void back_insert(char* in, int i) {
while (in[i - len[last] - 1] != in[i]) last = fail[last];
if (!tree[last][in[i] - 'a']) {
len[++num] = len[last] + 2;
int j = fail[last];
while (in[i - len[j] - 1] != in[i]) j = fail[j];
fail[num] = tree[j][in[i] - 'a'];
tree[last][in[i] - 'a'] = num;
mark[num] = mark[fail[num]] + 1;//维护以该节点为为节点的回文串数
}
last = tree[last][in[i] - 'a'];
if (len[last] == stdr - stdl + 1) pre = last;//若本身为回文串更新pre
sum = sum + mark[last];//维护sum
}
AC
#pragma warning (disable:4996)
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
using namespace std;
const int maxn = 100005;
const int base = maxn;
char in[maxn * 2];
int tree[maxn][26];
int len[maxn], fail[maxn];
int pre, last, num;
int stdl, stdr;
int mark[maxn]; long long sum;
void init() {
memset(in, 0, sizeof(in));
memset(mark, 0, sizeof(mark));
memset(tree, 0, sizeof(tree));
len[0] = 0; len[1] = -1;
fail[0] = 1; fail[1] = 0;
num = 1;
pre = last = 0;
sum = 0;
}
void pre_insert(char* in, int i) {
while (in[i + len[pre] + 1] != in[i]) pre = fail[pre];
if (!tree[pre][in[i] - 'a']) {
len[++num] = len[pre] + 2;
int j = fail[pre];
while (in[i + len[j] + 1] != in[i]) j = fail[j];
fail[num] = tree[j][in[i] - 'a'];
tree[pre][in[i] - 'a'] = num;
mark[num] = mark[fail[num]] + 1;
}
pre = tree[pre][in[i] - 'a'];
if (len[pre] == stdr - stdl + 1) last = pre;
sum = sum + mark[pre];
}
void back_insert(char* in, int i) {
while (in[i - len[last] - 1] != in[i]) last = fail[last];
if (!tree[last][in[i] - 'a']) {
len[++num] = len[last] + 2;
int j = fail[last];
while (in[i - len[j] - 1] != in[i]) j = fail[j];
fail[num] = tree[j][in[i] - 'a'];
tree[last][in[i] - 'a'] = num;
mark[num] = mark[fail[num]] + 1;
}
last = tree[last][in[i] - 'a'];
if (len[last] == stdr - stdl + 1) pre = last;
sum = sum + mark[last];
}
int main() {
int n, opt;
char s[5];
while (~scanf("%d", &n)) {
stdl = base + 1, stdr = base;
init();
while (n--) {
scanf("%d", &opt);
if (opt == 1) {
scanf("%s", s);
in[--stdl] = s[0];
pre_insert(in, stdl);
}
else if (opt == 2) {
scanf("%s", s);
in[++stdr] = s[0];
back_insert(in, stdr);
}
else if (opt == 3)
printf("%d\n", num - 1);
else
printf("%lld\n", sum);
}
}
}