hdu 5421 Victor and String

hdu 5421 Victor and String

支持双端插入的回文树。

考虑维护第二个 last 表示当前整个串的最长回文前缀。

往前 append 的时候可以直接那第二个 last 来跳 fail , 因为回文前缀和回文后缀是对称的。只有 getfail 的时候需要改一下,变成判断当前字符和前缀之后的字符是否相同。

还要注意,如果当前的前面/后面 的 last 是整个串,那么需要把另一个 last 也设置成整个串。

注意如果当前插入得到的回文串pam里面已经有了也要加上它的贡献!

#include<iostream>
#include<algorithm>
#include<cstring>
#include<cstdio>
using namespace std;
#define MAXN 300006
int n;
struct pam {
	int son[MAXN][26] , fail[MAXN] , dep[MAXN] , len[MAXN] , s[MAXN * 3];
	int last[2] , n = 0 , st = 100006 , en = st , p = 0; long long re = 0;
	int newnode(int l) {
        for (int i = 0; i < 26; i++)
            son[p][i] = 0;
        len[p] = dep[p] = l;
        return p++;
    }
	void init( ) {
		memset( s , -1 , sizeof s );
		p = re = 0;
		newnode( 0 ) , newnode( -1 );
		st = en = 100006;
		last[0] = last[1] = n = 0;
		fail[0] = 1;
	}
	int getfail1( int u ) {
		while( s[en - len[u] - 1] != s[en] ) u = fail[u];
		return u;
	}
	int getfail2( int u ) {
		while( s[st + len[u] + 2] != s[st + 1] ) {
			u = fail[u];
		}
		return u;
	}
	void add( int u , bool af ) { 
		u -= 'a';
		int cur;
		if( af ) {
			s[++ en] = u;
			cur = getfail1( last[af] ); 
		}
		else s[st --] = u , cur = getfail2( last[af] );
		if( !son[cur][u] ) {
			int now = newnode( len[cur] + 2 );
			fail[now] = son[af ? getfail1(fail[cur]) : getfail2(fail[cur])][u];
            son[cur][u] = now;
            dep[now] = dep[fail[now]] + 1 , re += dep[now];
		} else re += dep[son[cur][u]];
		last[af] = son[cur][u];
		if( len[last[af]] == en - st ) last[af^1] = last[af];
	}
	int dif( ) {
		return p - 2;
	}
	long long und( ) {
		return re;
	}
} pam ;

int main() {
	int op; char ch[3];
	while( cin >> n ) {
		pam.init();
		while( n -- ) {
			scanf("%d",&op);
			if( op == 1 ) {
				scanf("%s",ch) , pam.add( ch[0] , 0 );
			} else if( op == 2 ) {
				scanf("%s",ch) , pam.add( ch[0] , 1 );
			} else if( op == 3 ) {
				printf("%d\n",pam.dif( ));
			} else printf("%lld\n" , pam.und());
		}
	}
}
posted @ 2020-01-18 21:03  yijan  阅读(174)  评论(0编辑  收藏  举报