首页 > 技术文章 > [国家集训队]最长双回文串

hznumqf 2021-07-13 17:26 原文

[国家集训队]最长双回文串

题意

求长度\(n\)的串\(S\)的最长双回文串\(T\)

\(T\)能分为两部分\(X,Y\) (\(|X| \geq 1,|Y| \geq 1)\)\(X,Y\)都为回文串

分析

可以用回文自动机维护每个位置的最长回文后缀\(R[i]\)

那么答案就是\(max(R[i] + L[i + 1])\)

\(L[i]\)只需要翻转\(S\)再求一遍即可

代码

#include<bits/stdc++.h>
#define pii pair<int,int>
#define fi first
#define se second
#define eps 1e-9
#define db long double
#define equals(a,b) fabs(a-b) < eps
using namespace std;

typedef long long ll;



inline ll rd(){
	ll x;
	scanf("%lld",&x);
	return x;
}

const int maxn = 1e5 + 5;
char ss[maxn];
int L[maxn],R[maxn];

namespace pam {
	int sz, tot, last;
	int cnt[maxn], ch[maxn][26], len[maxn], fail[maxn];
	int R[maxn]; //rightmost len
	char s[maxn];
	inline int node(int l) {
  		sz++;
  		memset(ch[sz], 0, sizeof(ch[sz]));
  		len[sz] = l;
  		fail[sz] = cnt[sz] = 0;
  		return sz;
	}
	inline void clear() {
  		sz = -1;
  		last = 0;
  		s[tot = 0] = '$';
  		node(0);
  		node(-1);
  		fail[0] = 1;
	}
	inline int getfail(int x) {
  		while (s[tot - len[x] - 1] != s[tot]) x = fail[x];
  		return x;
	}
	inline void insert(char c,int idx) {
  		s[++tot] = c;
  		int now = getfail(last);
  		if (!ch[now][c - 'a']) {
    		int x = node(len[now] + 2);
    		fail[x] = ch[getfail(fail[now])][c - 'a'];
  	 	 	ch[now][c - 'a'] = x;
  			R[idx] = len[now] + 2;
		}
		else R[idx] = len[now] + 2;
  		last = ch[now][c - 'a'];
  		cnt[last]++;
	}
}

int main(){
	scanf("%s",ss + 1);
	int len = strlen(ss + 1);
	pam::clear();
	for(int i = 1;i <= len;i++)
		pam::insert(ss[i],i);
	int ans = 0;
	for(int i = 1;i <= len;i++){
		R[i] = pam::R[i];
	}
	pam::clear();
	reverse(ss + 1,ss + len + 1);
	for(int i = 1;i <= len;i++)
		pam::insert(ss[i],i);
	for(int i = 1;i <= len;i++){
		L[len - i + 1] = pam::R[i];
	}
	for(int i = 1;i < len;i++){
		ans = max(ans,R[i] + L[i + 1]);
	}
	cout << ans;
}

推荐阅读