[HDU5421]Victor and String(PAM)

题面

http://acm.hdu.edu.cn/showproblem.php?pid=5421

题解

前置知识

这道题很好地证明了PAM是可以双向添加的。

如果只有操作2、3、4,那就是PAM模板而已。现在考虑1怎么做。

发现操作1和2、以及3,4的维护并不冲突。可以仿效结尾加点的操作,具体如下:

  • 维护lastl,lastr表示当前的字符串的最长前缀回文子串和最长后缀回文子串。(可以为全串)
  • 更新fail,next的操作毫不影响,因为本来转移时回文串就是要左右两边各加一个字符
  • lastl、lastr有可能会相互影响,就是当lastl或者lastr其中之一求出来为全串的时候,那么也要把另一个也赋为全串。

更详细操作可见代码。

代码

#include<iostream>
#include<cstring>

using namespace std;

#define rg register
#define In inline
#define ll long long

const int N = 1e5;

In ll read(){
	ll s = 0,ww = 1;
	char ch = getchar();
	while(ch < '0' || ch > '9'){if(ch == '-')ww = -1;ch = getchar();}
	while('0' <= ch && ch <= '9'){s = 10 * s + ch - '0';ch = getchar();}
	return s * ww;
}

In void write(ll x){
	if(x < 0)putchar('-'),x = -x;
	if(x > 9)write(x / 10);
	putchar('0' + x % 10);
}

char s[2*N+5];
int l,r;

struct PAM{
	int nx[N+5][26],fail[N+5],len[N+5];
	ll dep[N+5];
	int cnt,lastl,lastr;
	ll sum;
	void clear(){
		cnt = 1;
		fail[0] = fail[1] = 1;
		memset(nx[0],0,sizeof(nx[0]));
		memset(nx[1],0,sizeof(nx[1]));
		len[0] = 0,len[1] = -1;
		lastl = lastr = 0;
		sum = 0;
	}
	void create(){
		cnt++;
		memset(nx[cnt],0,sizeof(nx[cnt]));
	}
	void extendl(char c,int n){
		int id = c - 'a';
		int p = lastl;
		while(s[l+len[p]+1] != s[l])p = fail[p];
		if(!nx[p][id]){
			create();
			len[cnt] = len[p] + 2;
			int q = fail[p];
			while(s[l+len[q]+1] != s[l])q = fail[q];
			fail[cnt] = nx[q][id];
			dep[cnt] = dep[fail[cnt]] + 1;
			nx[p][id] = cnt;
		}
		lastl = nx[p][id];
		sum += dep[lastl];
		if(len[lastl] == r - l + 1)lastr = lastl;
	}
	void extendr(char c,int n){
		int id = c - 'a';
		int p = lastr;
		while(s[r-len[p]-1] != s[r])p = fail[p];
		if(!nx[p][id]){
			create();
			len[cnt] = len[p] + 2;
			int q = fail[p];
			while(s[r-len[q]-1] != s[r])q = fail[q];
			fail[cnt] = nx[q][id];
			dep[cnt] = dep[fail[cnt]] + 1;
			nx[p][id] = cnt;
		}
		lastr = nx[p][id];
		sum += dep[lastr];
		if(len[lastr] == r - l + 1)lastl = lastr;
	}
}P;

int n;

int main(){
	while(~scanf("%d",&n)){
		P.clear();
		memset(s,0,sizeof(s));
		l = N,r = N - 1;
		while(n--){
			int opt = read();
			if(opt <= 2){
				char c = getchar();
				while(c < 'a' || c > 'z')c = getchar();
				if(opt == 1)s[--l] = c,P.extendl(c,l);
				else s[++r] = c,P.extendr(c,r);
			}
			else{
				if(opt == 3)write(P.cnt - 1),putchar('\n');
				else write(P.sum),putchar('\n');
			}
		}
	}
	return 0;
}
posted @ 2020-10-04 20:04  coder66  阅读(129)  评论(0编辑  收藏  举报