bzoj2565: 最长双回文串(manacher)

mac2022-06-30  122

原题链接

题目描述:顺序和逆序读起来完全一样的串叫做回文串。比如acbca是回文串,而abc不是(abc的顺序为“abc”,逆序为“cba”,不相同)。 输入长度为n的串S,求S的最长双回文子串T,即可将T分为两部分X,Y,(|X|,|Y|≥1)且X和Y都是回文串。

输入格式:一行由小写英文字母组成的字符串S。

输出格式:一行一个整数,表示最长双回文子串的长度。

样例输入: baacaabbacabb

样例输出: 12

解析:看到回文串似乎就会想到manacher。    首先跑一遍manacher。    关键是接下来怎么做,如果强行枚举断点会变成\(O(n^2)\)。    设l[i]表示右端点为i的回文串的最长长度,r[i]表示左端点为i的回文串的最长长度。    那么可以在manacher的同时求出l[i]和r[i]的初值。    l[i + hw[i] - 1] = max(l[i + hw[i] - 1], hw[i] - 1)    r[i - hw[i] + 1] = max(r[i - hw[i] + 1], hw[i] - 1)    最后再递推求出最终的l[i]和r[i]    l[i] = max(l[i], l[i + 2] - 2)    r[i] = max(r[i], r[i - 2] - 2)    最后枚举每一个添加进去的字符,答案即为max{l[i] + r[i]}。

代码如下:

#include<cstdio> #include<algorithm> #include<cstring> using namespace std; const int maxn = 1e5 + 5; int n, hw[maxn << 1], ans, l[maxn << 1], r[maxn << 1]; char s[maxn << 1]; void prepare(void) { s[n << 1] = '#'; for (int i = n; i ; -- i) { s[i * 2 - 1] = s[i]; s[i * 2 - 2] = '#'; } n <<= 1; } void manacher(void) { prepare(); int mid = 0, mr = 0; for (int i = 1; i <= n; ++ i) { if (i < mr) hw[i] = min(hw[mid * 2 - i], mr - i); else hw[i] = 1; while (i - hw[i] >= 0 && i + hw[i] <= n && s[i - hw[i]] == s[i + hw[i]]) hw[i] ++; if (i + hw[i] - 1 > mr) { mr = i + hw[i] - 1; mid = i; } } } int main() { scanf("%s", s + 1); n = strlen(s + 1); manacher(); for (int i = 1; i <= n; ++ i) { //处理出初值 l[i + hw[i] - 1] = max(l[i + hw[i] - 1], hw[i] - 1); r[i - hw[i] + 1] = max(r[i - hw[i] + 1], hw[i] - 1); } for (int i = 0; i <= n; i += 2) r[i] = max(r[i], r[i - 2] - 2); //递推求出l[i]和r[i] for (int i = n; i >= 0; i -= 2) l[i] = max(l[i], l[i + 2] - 2); for (int i = 0; i <= n; i += 2) //注意l[i]和r[i]都不能为0! if (l[i] && r[i]) ans = max(ans, l[i] + r[i]); printf("%d", ans); return 0; }

转载于:https://www.cnblogs.com/Gaxc/p/10145787.html

相关资源:JAVA上百实例源码以及开源项目
最新回复(0)