首先,我们维护一个右端点具有最大下标的回文串的中心下标 id 和右端点下标 r,记 d[i] 表示以下标 i 为中心的最长奇数长度回文串的半径大小。
枚举中心 i,假如当前枚举的下标 i<r,那么由回文串的性质,将 i 以 id 为中心对称过去得到对应的下标 j=id∗2−i,则几乎可以认为 d[i]=d[j],在下图中这是很显然的(图片来自 oi-wiki):
不过考虑到 j 的最长回文串的左端点可能落在 id 的最长回文串之外,因此我们需要砍掉多出去的部分,然后再暴力向外扩展 d[i]。
另一种情况是,如果当前枚举的下标 i≥r,那么就直接暴力枚举。
最后都要记得用 i 的结果更新 id 和 r。
Complexity
在以上过程中,只要 i 的最长回文串是完整落在 id 的最长回文串内部的,那么对于 i 的求解是 O(1) 的,而一旦 i 的最长回文串落在了 id 的最长回文串外,我们只会枚举落在外面的部分,并且枚举完成后会立即更新 id 和 r,也就是说每个下标最多只会被枚举两次,一次是成为左端点,另一次是成为右端点,所以 manacher 算法是 O(n) 的。
manacher(); for (int i = 2; i <= n; i += 2) l[i] = std::max(l[i], l[i - 2] - 2); for (int i = n - 2; i > 0; i -= 2) r[i] = std::max(r[i], r[i + 2] - 2);
int ans = 0; for (int i = 2; i <= n; i += 2) if (l[i] && r[i]) ans = std::max(ans, l[i] + r[i]);
using std::cin; using std::cout; using std::string; using std::vector; using i64 = longlong;
constint mod = 19930726; constint N = 1e6 + 5;
int n; i64 k, ans; string s; vector <int> r, d;
voidmanacher(){ r = vector <int> (n); for (int i = 0, id = 0, rr = -1; i < n; ++i) { r[i] = (i < rr) ? std::min(r[(id << 1) - i], rr - i + 1) : 1; while (i - r[i] >= 0 && i + r[i] < n && s[i - r[i]] == s[i + r[i]]) ++r[i]; if (i + r[i] - 1 > rr) id = i, rr = i + r[i] - 1; } }
i64 qpow(i64 a, int b){ i64 ret = 1; while (b) { if (b & 1) ret = ret * a % mod; a = a * a % mod; b >>= 1; } return ret; }
voidcount(){ int cnt = 0; i64 tot = 0; ans = 1;
for (int len = r[0], p = 0; len > 0 && tot < k; len -= 2) { while (p >= 0 && r[p] == len) cnt++, p++; if (cnt > k - tot) cnt = k - tot; ans = ans * qpow(1ll * len, cnt) % mod; tot += cnt; } }
Comments