欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

「UVA 11475」Extend to Palindrome「后缀数组」

程序员文章站 2022-04-17 14:37:07
...

这题算是经典题了,可以用KMP/Manacher/SAKMP/Manacher/SA

这里丢上一个代码最长的做法:后缀数组

做法是将原串ss翻转得到ss',将ss'接到ss后做SASA

这样就会有一个很好的性质:我们可以快速求出以某个点或某两个点为中心的最长回文串长度。以xx为中心的答案为LCP(suffix[x],suffix[x])\text{LCP}(\text{suffix}[x],\text{suffix}[x'])xx'xx翻转后的位置

例如 s=cabab,s=babacs=cabab,s'=babac

求以s4s_4为中心的最长回文串长度

显然是babbab,答案为33,可以通过LCP(ab,abac)=2\text{LCP}(\text{ab},\text{abac})=2×2+1\times2+1得到

长度偶数为偶数的回文串也是一样的求法.

现在再分析一下这题应该怎么补:在末尾找一个最长回文串,以这个回文串的中心为中心补全,这样显然需要补全的字符最少

最后提醒注意:求lcp\text{lcp}后一定要这个后缀的实际长度取min\min,或者ssss'间加分隔符也行。

#include <algorithm>
#include <cstring>
#include <cstdio>
using namespace std;

#define rep(i, j, k) for(int i = j; i <= k; ++ i)

namespace Suffix_Array {
	const int N = 4e5 + 5;
	char s[N], s1[N], s2[N], ch[N];
	int n, m, t[N], sa[N], rk[N], ht[N];
	int cnt[N], fir[N], sec[N];
	void buildsa() {
		rep(i, 1, n) ch[i] = s[i];
		sort(ch + 1, ch + n + 1);
		int l = unique(ch + 1, ch + n + 1) - (ch + 1);
		rep(i, 1, n) t[i] = lower_bound(ch + 1, ch + l + 1, s[i]) - ch;
		rep(i, 1, n) cnt[i] = 0;
		rep(i, 1, n) ++ cnt[t[i]];
		rep(i, 1, n) cnt[i] += cnt[i - 1];
		rep(i, 1, n) rk[i] = cnt[t[i] - 1] + 1;
		for(int k = 1; k <= n; k <<= 1) {
			rep(i, 1, n) fir[i] = rk[i];
			rep(i, 1, n) sec[i] = i + k > n ? 0 : rk[i + k];

			rep(i, 1, n) cnt[i] = 0;
			rep(i, 1, n) ++ cnt[sec[i]];
			rep(i, 1, n) cnt[i] += cnt[i - 1];
			rep(i, 1, n) t[n - -- cnt[sec[i]]] = i;

			rep(i, 1, n) cnt[i] = 0;
			rep(i, 1, n) ++ cnt[fir[i]];
			rep(i, 1, n) cnt[i] += cnt[i - 1];
			rep(i, 1, n) sa[cnt[fir[t[i]]] --] = t[i];

			bool dif = true; rk[sa[1]] = 1;
			rep(i, 2, n) {
				int & v = sa[i - 1], & u = sa[i];
				rk[u] = rk[v];
				if(fir[u] == fir[v] && sec[u] == sec[v]) dif = false;
				else ++ rk[u];
			}
			if(dif) break ;
		}
		for(int i = 1, k = 0; i <= n; i ++) {
			if(k) -- k;
			int j = sa[rk[i] - 1];
			for(; i + k <= n && j + k <= n && s[i + k] == s[j + k]; ++ k) ;
			ht[rk[i]] = k;
		}
	}
	int f[N][30], lg[N];
	void buildst() {
		lg[1] = 0;
		rep(i, 2, n) lg[i] = lg[i >> 1] + 1;
		rep(i, 1, n) f[i][0] = ht[i];
		for(int j = 1; (1 << j) <= n; ++ j)
			for(int i = 1; i + (1 << j) - 1 <= n; ++ i)
				f[i][j] = min(f[i][j - 1], f[i + (1 << j - 1)][j - 1]);
	}
	int lcp(int a, int b) {
		a = rk[a]; b = rk[b];
		if(a > b) swap(a, b);
		++ a;
		int l = lg[b - a + 1];
		return min(f[a][l], f[b - (1 << l) + 1][l]);
	}
}
using namespace Suffix_Array;

//求以x(odd = 1)或x, x + 1(odd = 0)为中心的最长回文串长度 
int maxstr(int x, bool odd) {
	int y = odd ? n + 1 - x : n - x; //在后半部分对应点
	return lcp(x, y);
}

int main() {
	while(~ scanf("%s", s1 + 1)) {
		m = strlen(s1 + 1); n = m + m;
		rep(i, 1, m) s2[i] = s1[i];
		reverse(s2 + 1, s2 + m + 1);
		rep(i, 1, m) s[i] = s1[i];
		rep(i, 1, m) s[i + m] = s2[i];
		buildsa(); buildst();

		int pos = m + 1;
		rep(i, 1, m) {
			int r = m - i, w1 = maxstr(i, true), w2 = maxstr(i, false);
			w1 = min(w1, r + 1); w2 = min(w2, r + 1); //注意取min
			if(r + 1 == w1) pos = min(pos, i - r);
			if(i != m && r + 1 == w2) pos = min(pos, i - r + 1);
		}
		s[pos] = '\0';
		reverse(s + 1, s + pos);
		printf("%s%s\n", s1 + 1, s + 1);
	}
	return 0;
}
相关标签: 后缀数组