题意:求给定的字符串的最长回文子串
分析:做法是构造一个新的字符串是原字符串+反转后的原字符串(这样方便求两边回文的后缀的最长前缀),即newS = S + '$' + revS,枚举回文串中心位置,RMQ询问LCP = min (height[rank[l]+1] to height[rank[r]]),注意的是RMQ传入参数最好是后缀的位置,因为它们在树上的顺序未知,且左边还要+1。
#include <cstdio> #include <algorithm> #include <cstring> const int N = 1e3 + 10; const int D = 21; char s[N]; int sa[N<<1], t[N<<1], t2[N<<1], c[N<<1], n; int rank[N<<1], height[N<<1]; int dp[N<<1][D]; void da(char *s, int m = 256) { int i, *x = t, *y = t2; for (i=0; i<m; ++i) c[i] = 0; for (i=0; i<n; ++i) c[x[i]=s[i]]++; for (i=1; i<m; ++i) c[i] += c[i-1]; for (i=n-1; i>=0; --i) sa[--c[x[i]]] = i; for (int k=1; k<=n; k<<=1) { int p = 0; for (i=n-k; i<n; ++i) y[p++] = i; for (i=0; i<n; ++i) if (sa[i] >= k) y[p++] = sa[i] - k; for (i=0; i<m; ++i) c[i] = 0; for (i=0; i<n; ++i) c[x[y[i]]]++; for (i=0; i<m; ++i) c[i] += c[i-1]; for (i=n-1; i>=0; --i) sa[--c[x[y[i]]]] = y[i]; std::swap (x, y); p = 1; x[sa[0]] = 0; for (i=1; i<n; ++i) { x[sa[i]] = y[sa[i-1]] == y[sa[i]] && y[sa[i-1]+k] == y[sa[i]+k] ? p - 1 : p++; } if (p >= n) break; m = p; } //height[i] = LCP (s[sa[i-1]], s[sa[i]]); int j, k = 0; for (i=0; i<n; ++i) rank[sa[i]] = i; for (i=0; i<n; ++i) { if (k) k--; int j = sa[rank[i]-1]; while (s[i+k] == s[j+k]) k++; height[rank[i]] = k; } } int query_RMQ(int l, int r) { l = rank[l]; r = rank[r]; if (l > r) { std::swap (l, r); } l++; int k = 0; while (1<<(k+1) <= r - l + 1) k++; return std::min (dp[l][k], dp[r-(1<<k)+1][k]); } void init_RMQ(int *height) { //memset (dp, 0, sizeof (dp)); for (int i=0; i<=n; ++i) { dp[i][0] = height[i]; } for (int j=1; (1<<j)<=n; ++j) { for (int i=0; i+(1<<j)<=n; ++i) { dp[i][j] = std::min (dp[i][j-1], dp[i+(1<<(j-1))][j-1]); } } } int run(char *s) { int len = strlen (s); n = len; s[n++] = '$'; for (int i=len-1; i>=0; --i) { s[n++] = s[i]; } s[n++] = '\0'; da (s); init_RMQ (height); int ret = 0; for (int i=0; i<len; ++i) { int l = query_RMQ (i, n-i-2); //奇数 ret = std::max (ret, 2 * l - 1); l = query_RMQ (i, n-i-1); //偶数 ret = std::max (ret, 2 * l); } return ret; } int main() { int cas = 0; while (scanf ("%s", s) == 1) { if (strcmp (s, "END") == 0) { break; } printf ("Case %d: %d\n", ++cas, run (s)); } return 0; }
URAL 1297 题目没什么区别,数据规模小了,还要输出回文串。
核心代码
int best = 0; from = 0; to = 1; for (int i=0; i<len; ++i) { int l = query_RMQ (i, n-i-1); if (best < 2 * l - 1) { from = i - l + 1; to = i + l; best = 2 * l - 1; } l = query_RMQ (i, n-i); if (best < 2 * l) { from = i - l; to = i + l; best = 2 * l; } }