最牛的回文
Description
据说,如果有无穷多的母牛和无穷多的大型键盘,它们就可以创造出世界上最伟大的回文。在寻找回文时,可以不计文中的标点、空白和大小写,只要关注 26 个英文字母就可以了。但是要注意,在输出时要按照原样,也就是要保留原有的空白、标点和大小写。你的任务,就是在不超过 20000 个字符的字符串中,寻找长度不超过 2000 的回文字符串(含空格和标点时)。
Input
一段文本,不超过 20000 个字符,可以有一行或多行,每行的长度不超过 80 个字符。
Output
输出的第一行为找到的最长的回文字符串的长度。后面的行应该包括该字符串,字符串两边多余的空格和标点都不需要输出,但字符串中的空格、标点和换行则需要按照原样输出。如果文中有多个长度相同的回文字符串,只要输出第一个就可以了。
Sample Input
Sonic: Makam, I'm Akam.
Sample Output
11
Makam, I'm Akam
分析:最长回文串问题,利用manacher算法复杂度为O(n)。关键在找最长时,要注意回文半径和实际回文长度的区别。另外就是通过穿插字符使回文长度奇偶统一化。
#include<stdio.h> #include<string.h> #define MAXN 40010 char a[MAXN], b[MAXN], c[MAXN * 2]; int pos[MAXN], r[MAXN * 2]; int len1, len2, len3; int main() { int i, j, k, maxr, start, end, s, e; char ch; while (scanf("%c", &ch) != EOF) { // if (ch == '1') // break; a[len1++] = ch; } for (i = 0; i < len1; ++i) { if (a[i] >= 'a' && a[i] <= 'z') { b[len2] = a[i]; pos[len2++] = i; } else if (a[i] >= 'A' && a[i] <= 'Z') { b[len2] = a[i] + 32; pos[len2++] = i; } } for (i = 0; i < len2; ++i) c[0] = '~'; for (i = 0; i < len2; ++i) { c[2 * i + 1] = b[i]; c[2 * i + 2] = '!'; } c[2 * len2] = '@'; len3 = len2 * 2 + 1; j = 0; for (i = 1; i < len3 - 1; i += k) { while (c[i - j - 1] == c[i + j + 1]) ++j; r[i] = j; for (k = 1; k < r[i]; ++k) { if (r[i] - k == r[i - k]) break; if (r[i] - k < r[i - k]) r[i + k] = r[i] - k; else r[i + k] = r[i - k]; } j = r[i] - k > 0 ? (r[i] - k) : 0; } maxr = -1; for (i = 1; i < len3 - 1; ++i) { if (c[i + r[i]] >= 'a' && c[i + r[i]] <= 'z') { if (r[i] + 1 > maxr) { start = pos[(i - r[i]) / 2]; end = pos[(i + r[i]) / 2]; if (end - start + 1 > 2000) continue; maxr = r[i] + 1; s = start; e = end; } } else { if (r[i] > maxr) { start = pos[(i - r[i]) / 2]; end = pos[(i + r[i]) / 2 - 1]; if (end - start + 1 > 2000) continue; maxr = r[i]; s = start; e = end; } } } printf("%d\n", maxr); for (i = start; i <= end; ++i) printf("%c", a[i]); printf("\n"); return 0; }