HDU5421 Victor and String(回文自动机前端插入)

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 num1
    • 字符串总数维护:每增加一个节点增加以该节点为尾节点的回文串数

代码

初始化
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);
		}
	}
}
  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值